package com.lh.service.impl;

import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lh.entity.table.*;
import com.lh.entity.vo.*;
import com.lh.mapper.*;
import com.lh.service.*;
import com.lh.utils.*;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import org.springframework.core.io.ClassPathResource;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import org.springframework.transaction.annotation.Transactional;

import static com.lh.utils.DateAddDemo.calculateFutureDate;
import static java.lang.System.out;

import cn.hutool.core.date.DateUtil;

import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

@Service
public class RectificationInfoServiceImpl implements RectificationInfoService {

    @Resource
    private RectificationInfoMapper rectificationInfoMapper;

    @Resource
    private Task2InfoMapper task2InfoMapper;
    @Autowired
    private DeadLineNodeRecordService deadLineNodeRecordService;
    @Autowired
    private DeadLineNodeRecordMapper deadLineNodeRecordMapper;

    @Autowired
    private CompanyDataMapper companyDataMapper;
    @Autowired
    private EnterpriseDataMapper enterpriseDataMapper;
    @Resource
    private Task1InfoMapper task1InfoMapper;

    @Resource
    private TaskInfoMapper taskInfoMapper;

    @Resource
    private DeadlineNodeMapper deadlineNodeMapper;
    @Resource
    private DeadlineRectificationPeriodService deadlineRectificationPeriodService;
    @Resource
    private TicketProblemInfoService ticketProblemInfoService;

    @Resource
    private TicketProblemInfoMapper ticketProblemInfoMapper;

    @Resource
    private ProblemSummaryInfoMapper problemSummaryInfoMapper;

    @Autowired
    private ResponsiblePersonsServiceImpl responsiblePersonsService;

    @Resource
    private DeadlineRectificationPeriodMapper deadlineRectificationPeriodMapper;

    @Resource
    private DeadlineNodeUpdateServer deadlineNodeUpdateServer;

    @Resource
    private ReminderPersonMapper reminderPersonMapper;

    @Resource
    private DeadlineNodeUpdateMapper deadlineNodeUpdateMapper;

    @Resource
    private SignReceiptMapper signReceiptMapper;

    @Resource
    private ReminderInfoMapper reminderInfoMapper;

    @Resource
    private UpdateProgressMapper updateProgressMapper;

    @Resource
    private UpdateProgressService updateProgressService;

    private LogUtil LOG = new LogUtil(ExcelServiceImpl.class);

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    //task2
    // rectification

    @Transactional
    @Override
    public Answer<Object> checkName(RectificationInfo rectificationInfo) {
        LambdaQueryWrapper<RectificationInfo> rectificationInfoLambdaQueryWrapper =
                new LambdaQueryWrapper<>();
        List<RectificationInfo> rectificationInfos = rectificationInfoMapper.selectList(rectificationInfoLambdaQueryWrapper);
        boolean hasSimilar = hasSimilarName(rectificationInfo.getTitle(), rectificationInfos);
        if (hasSimilar) {
            return Answer.ok("存在相似度超过70%的名称");
        } else {
            return Answer.ok("");
        }
    }

    @Transactional
    @Override
    public Answer<Object> add(RectificationInfo rectificationInfo) {
        Date date = new Date();
        //项目名称
        String title = rectificationInfo.getTitle();
        if (StrUtil.isBlank(title)) {
            return Answer.error("项目名称不能为空");
        }

        //项目简介
        String content = rectificationInfo.getContent();
        if (StrUtil.isBlank(content)) {
            return Answer.error("项目简介不能为空");
        }

        //牵头单位
        String leadUnit = rectificationInfo.getLeadUnit();
        if (StrUtil.isBlank(leadUnit)) {
            return Answer.error("牵头单位不能为空");
        }

        //工作类型
        String projectType = rectificationInfo.getProjectType();
        if (StrUtil.isBlank(projectType)) {
            return Answer.error("工作类型不能为空");
        }

        //牵头领导
        String leadLeader = rectificationInfo.getLeadLeader();
        if (StrUtil.isBlank(leadLeader)) {
            return Answer.error("牵头领导不能为空");
        }

        //责任人
        String personLiaible = rectificationInfo.getPersonLiaible();
        if (StrUtil.isBlank(personLiaible)) {
            return Answer.error("责任人不能为空");
        }

        //项目要求完成时间
        Date deadline = rectificationInfo.getDeadline();
//        if (deadline == null) {
//            return Answer.error("项目要求完成时间不能为空");
//        }


        String[] split = projectType.split("/");


        rectificationInfo.setProjectType(split[0]);
        rectificationInfo.setProjectChildType(split[1]);
        if (split.length > 2) {
            rectificationInfo.setProjectChildCategoryType(split[2]);
        }
        rectificationInfo.setCheckRemark(rectificationInfo.getCheckRemark());
        rectificationInfo.setState("待提交");


        //List<ProblemSummaryInfo> problemSummaryInfoList = problemSummaryInfoMapper.selectList(new LambdaQueryWrapper<ProblemSummaryInfo>().eq(ProblemSummaryInfo::getProjectName, projectType));
        Integer summaryId = 1;
        TaskInfo taskInfo = new TaskInfo();
        taskInfo.setContent(content);
        taskInfo.setName(content);
        taskInfo.setSummaryId(summaryId);
        taskInfo.setCreateTime(date);
        //TaskInfo lastTaskInfo = taskInfoMapper.selectOne(new LambdaQueryWrapper<TaskInfo>().eq(TaskInfo::getSummaryId, summaryId).isNotNull(TaskInfo::getSort).orderByDesc(TaskInfo::getSort).last("LIMIT 1"));
        taskInfo.setSort(1);
        taskInfoMapper.insert(taskInfo);


        Task1Info task1Info = new Task1Info();
        task1Info.setTaskId(taskInfo.getId());
        task1Info.setContent(content);
        task1Info.setName(content);
        task1Info.setCreateTime(date);
        //Task1Info lastTask1Info = task1InfoMapper.selectOne(new LambdaQueryWrapper<Task1Info>().eq(Task1Info::getTaskId, taskInfo.getId()).isNotNull(Task1Info::getSort).orderByDesc(Task1Info::getSort).last("LIMIT 1"));
        task1Info.setSort(1);
        task1InfoMapper.insert(task1Info);


        Task2Info task2Info = new Task2Info();
        task2Info.setTask1Id(task1Info.getId());
        task2Info.setContent(content);
        task2Info.setName(title);
        task2Info.setCreateTime(date);
        task2Info.setSort(1);
        task2InfoMapper.insert(task2Info);
        rectificationInfo.setIsEdit(1);
        rectificationInfo.setSummaryId(summaryId);
        rectificationInfo.setTask2Id(task2Info.getId());
        rectificationInfoMapper.insert(rectificationInfo);
        if (rectificationInfo.getProjectType().equals("高质量发展")) {
            if (rectificationInfo.getProjectChildCategoryType() != null) {
                List<String> targetTypes = Arrays.asList(
                        "拟开工重点项目",
                        "谋划推进重大项目",
                        "签约未开工重点项目",
                        "招商签约未开工重点项目",
                        "谋划重大项目"
                );
                // 2. 获取当前类型并判空，用 Stream 流判断是否存在模糊匹配
                String currentType = rectificationInfo.getProjectChildCategoryType();
                boolean isMatch = Optional.ofNullable(currentType) // 处理null，避免空指针
                        .filter(type -> !type.trim().isEmpty()) // 过滤空字符串（可选，根据业务需求）
                        .map(type -> targetTypes.stream()
                                .anyMatch(targetType -> type.contains(targetType)) // 核心：任意一个目标类型被包含即匹配
                        ).orElse(false); // 若currentType为null或空，默认返回false
                // 3. 后续逻辑（同上）
                if (isMatch) {
                    // 满足条件的业务处理
                    DateTime dateTime = DateUtil.date(deadline);
                    nodeList(rectificationInfo, dateTime);
                }
            }
        }
        return Answer.ok("添加成功");
    }

    @Override
    public Answer<Object> addRectification(RectificationInfo rectificationInfo) {
        try {
//            Integer departmentId = rectificationInfo.getDepartmentId();
//            if(departmentId == null){
//                return Answer.error("责任单位不能为空");
//            }
            String content = rectificationInfo.getContent();
            if (StrUtil.isBlank(content)) {
                return Answer.error("问题表现不能为空");
            }
            String departmentName = rectificationInfo.getDepartmentName();
            if (departmentName == null) {
                return Answer.error("责任单位名称不能为空");
            }
            Date timeLimit = rectificationInfo.getTimeLimit();
            if (timeLimit == null) {
                return Answer.error("整改时限不能为空");
            }
            Integer task2Id = rectificationInfo.getTask2Id();
            if (task2Id == null) {
                return Answer.error("任务不能为空");
            }
            Integer summaryId = rectificationInfo.getSummaryId();
            if (summaryId == null) {
                return Answer.error("问题大类不能为空");
            }
            rectificationInfoMapper.insert(rectificationInfo);
            return Answer.ok("添加成功");
        } catch (Exception e) {
            return Answer.error("添加失败");
        }
    }

    @Override
    public Answer<Object> getRectificationList(JSONObject reqJson) {
        try {
            ArrayList<Object> resList = CollectionUtil.newArrayList();

            Integer pageSize = reqJson.getInteger("pageSize"); // 默认10条/页
            Integer pageNum = reqJson.getInteger("pageNum");    // 默认第1页
            if (pageNum < 1) pageNum = 1; // 防止pageNum为0或负数
            // 2. 初始化最终返回的分页对象
            Page<JSONObject> resultPage = new Page<>(pageNum, pageSize);

            String departmentName = reqJson.getString("departmentName");

            if (StrUtil.isNotBlank(departmentName)) {
                // 按部门查询：使用MyBatis-Plus的Page对象传入mapper
                Page<RectificationInfo> queryPage = new Page<>(pageNum, pageSize);
                LambdaQueryWrapper<RectificationInfo> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(RectificationInfo::getDepartmentName, departmentName)
                        .orderByAsc(RectificationInfo::getSort);

                // 关键：mapper返回的page对象已包含分页信息（总条数、当前页数据等）
                Page<RectificationInfo> rectificationInfoPage = rectificationInfoMapper.selectPage(queryPage, queryWrapper);

                // 处理当前页数据
                List<JSONObject> currentPageData = rectificationInfoPage.getRecords().stream().map(info -> {
                    Task2Info task2Info = task2InfoMapper.selectById(info.getTask2Id());
                    JSONObject json = new JSONObject();
                    json.put("rectificationInfo", info);
                    json.put("task2Id", task2Info.getId());
                    json.put("task2Name", task2Info.getName());
                    json.put("task2Content", task2Info.getContent());
                    Integer summaryId = info.getSummaryId();
                    json.put("summaryId", summaryId);
                    ProblemSummaryInfo problemSummaryInfo = problemSummaryInfoMapper.selectById(summaryId);
                    json.put("summaryName", problemSummaryInfo.getProjectName());
                    json.put("projectCategory", problemSummaryInfo.getProjectCategory());
                    return json;
                }).collect(Collectors.toList());

                // 填充结果分页信息
                resultPage.setRecords(currentPageData);
                resultPage.setTotal(rectificationInfoPage.getTotal());
                resultPage.setPages(rectificationInfoPage.getPages());

            } else {
                // 不按部门查询：同理处理分页
                Page<Task2Info> queryPage = new Page<>(pageNum, pageSize);
                LambdaQueryWrapper<Task2Info> task2LambdaQueryWrapper = new LambdaQueryWrapper<>();
                String title = reqJson.getString("title");
                if (StrUtil.isNotBlank(title)) {
                    task2LambdaQueryWrapper.like(Task2Info::getName, title);
                }

                // 关键：mapper返回的page对象包含分页信息
                Page<Task2Info> task2InfoPage = task2InfoMapper.selectPage(queryPage, task2LambdaQueryWrapper);

                // 处理当前页数据
                List<JSONObject> currentPageData = task2InfoPage.getRecords().stream().map(task2Info -> {
                    JSONObject json = new JSONObject();
                    Integer id = task2Info.getId();
                    RectificationInfo rectificationInfoRes = rectificationInfoMapper.selectOne(
                            new LambdaQueryWrapper<RectificationInfo>().eq(RectificationInfo::getTask2Id, id)
                    );
                    json.put("task2Id", task2Info.getId());
                    json.put("task2Name", task2Info.getName());
                    json.put("task2Content", task2Info.getContent());
                    json.put("rectificationInfo", rectificationInfoRes);

                    if (rectificationInfoRes != null) {
                        Integer summaryId = rectificationInfoRes.getSummaryId();
                        json.put("summaryId", summaryId);
                        ProblemSummaryInfo problemSummaryInfo = problemSummaryInfoMapper.selectById(summaryId);
                        json.put("summaryName", problemSummaryInfo.getProjectName());
                        json.put("projectCategory", problemSummaryInfo.getProjectCategory());
                    } else {
                        json.put("projectCategory", null);
                        json.put("summaryId", null);
                        json.put("summaryName", null);
                    }
                    return json;
                }).collect(Collectors.toList());

                // 填充结果分页信息
                resultPage.setRecords(currentPageData);
                resultPage.setTotal(task2InfoPage.getTotal());
                resultPage.setPages(task2InfoPage.getPages());
            }
            // 将处理后的结果列表放入分页对象
//            resultPage.setRecords(resList.stream().map(obj -> (JSONObject) obj).collect(Collectors.toList()));
            return Answer.ok(resultPage);
        } catch (Exception e) {
            e.printStackTrace();
            return Answer.error("查询失败");
        }
    }

    @Override
    public Answer<Object> updateRectification(RectificationInfo rectificationInfo) {
        try {

            Integer id = rectificationInfo.getId();
            if (id == null) {
                return Answer.error("id不能为空");
            }
            if (StrUtil.isNotBlank(rectificationInfo.getProjectType())) {
                String[] split = rectificationInfo.getProjectType().split("/");
                if (split.length > 1) {
                    rectificationInfo.setProjectType(split[0]);
                    rectificationInfo.setProjectChildType(split[1]);
                    if (split.length > 2) {
                        rectificationInfo.setProjectChildCategoryType(split[2]);
                    }
                } else {
                    rectificationInfo.setProjectType("");
                }

            }
            rectificationInfoMapper.updateById(rectificationInfo);
            UpdateProgress updateProgress = new UpdateProgress();
            updateProgress.setProblemProgress(rectificationInfo.getProgressAndProblem());
            updateProgress.setRectificationId(""+rectificationInfo.getId());
            updateProgress.setCreateTime(new Date());
            updateProgressService.insertUpdateProgress(updateProgress);
            return Answer.ok("更新成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Answer.error("更新失败");
        }
    }

    @Override
    public Answer<Object> AddDeadlineNode(JSONObject reqJson) {
        try {
            JSONArray data = reqJson.getJSONArray("data");
            for (int i = 0; i < data.size(); i++) {
                JSONObject jsonObject = data.getJSONObject(i);
                DeadlineNode deadlineNode = JSONObject.parseObject(jsonObject.toJSONString(), DeadlineNode.class);
                String rectificationTime = jsonObject.getString("rectificationTime");
                Integer mainId = deadlineNode.getMainId();
//                Date deadline = deadlineNode.getDeadline();
                if (mainId == null) {
                    continue;
                }
//                if (deadline == null){
//                    continue;
//                }
                if (i == 0) {
                    RectificationInfo rectificationInfo = rectificationInfoMapper.selectById(mainId);
                    if (rectificationInfo == null) {
                        continue;
                    }
                    deadlineNodeMapper.delete(new LambdaQueryWrapper<DeadlineNode>().eq(DeadlineNode::getMainId, mainId).eq(DeadlineNode::getTypeName, "整改"));
                }
                deadlineNode.setCreateTime(new Date());
                deadlineNode.setSort(i);
                deadlineNode.setWarnLevel("0");
                deadlineNode.setState("1");
                deadlineNode.setRectificationId(mainId);
                deadlineNode.setTypeName("整改");
                deadlineNodeMapper.insert(deadlineNode);

                if (StrUtil.isNotBlank(rectificationTime)) {
                    Integer nodeId = deadlineNode.getId();
                    DeadlineRectificationPeriod deadlineRectificationPeriod = new DeadlineRectificationPeriod();
                    deadlineRectificationPeriod.setDeadlineNodeId(nodeId);
                    if (rectificationTime.length() < 15) {
                        rectificationTime = rectificationTime + " 23:59:59";
                    }
                    deadlineRectificationPeriod.setRectificationTime(DateUtil.parseDateTime(rectificationTime));
                    deadlineRectificationPeriodService.addDeadlineRectificationPeriod(deadlineRectificationPeriod);
                }
            }
            return Answer.ok("添加成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Answer.error("添加失败");
        }
    }

    @Override
    public Answer<Object> distribute(JSONObject reqJson) {
        try {
            JSONArray data = reqJson.getJSONArray("data");
            Integer id = null;
            out.println(data);
            for (int i = 0; i < data.size(); i++) {
                JSONObject jsonObject = data.getJSONObject(i);
                out.println(jsonObject);
                DeadlineNode deadlineNode = JSONObject.parseObject(jsonObject.toJSONString(), DeadlineNode.class);
                out.println(deadlineNode);
                Integer mainId = deadlineNode.getMainId();
                Date deadline = deadlineNode.getDeadline();
                String departmentName = deadlineNode.getDepartmentName();
                if (mainId == null) {
                    continue;
                }
                if (deadline == null) {
                    continue;
                }
                if (StrUtil.isBlank(departmentName)) {
                    continue;
                }
                RectificationInfo rectificationInfo = rectificationInfoMapper.selectById(mainId);
                out.println(rectificationInfo);
                if (rectificationInfo == null) {
                    continue;
                }
//                Integer summaryId = getSummaryId(rectificationInfo.getTask2Id());
//                ProblemSummaryInfo problemSummaryInfo = problemSummaryInfoMapper.selectById(summaryId);
//                if (problemSummaryInfo == null) {
//                    continue;
//                }
                if (i == 0) {
                    TicketProblemInfo ticketProblemInfo = new TicketProblemInfo();
                    ticketProblemInfo.setProminentIssue(rectificationInfo.getProjectType());
                    ticketProblemInfo.setContent(rectificationInfo.getContent());
                    ticketProblemInfo.setSummaryId(1);
                    ticketProblemInfo.setTask2Id(rectificationInfo.getTask2Id());
                    ticketProblemInfo.setLeadUnit(rectificationInfo.getLeadLeader());
                    ticketProblemInfo.setProcessHandlerDepartment(departmentName);
                    ticketProblemInfo.setStatus("0");
                    ticketProblemInfo.setCompleteDeadline(rectificationInfo.getDeadline());
                    Answer<Object> objectAnswer = ticketProblemInfoService.addTicketProblemInfo(ticketProblemInfo);
                    if (objectAnswer.getCode() == 200) {
                        id = (Integer) objectAnswer.getResult();
                    } else {
                        return Answer.error(objectAnswer.getMsg());
                    }
                }
                if (id != null) {
                    deadlineNode.setCreateTime(new Date());
                    deadlineNode.setSort(i);
                    deadlineNode.setState("1");
                    deadlineNode.setRectificationId(rectificationInfo.getId());
                    deadlineNode.setMainId(id);
                    deadlineNode.setTypeName("下发");
                    deadlineNodeMapper.insert(deadlineNode);
                }
            }
            return Answer.ok("添加成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Answer.error("添加失败");
        }
    }

    @Override
    public Answer<Object> queryDeadlineNodeList(RectificationInfo rectificationInfo) {
        try {
            Integer id = rectificationInfo.getId();
            if (id == null) {
                return Answer.error("id不能为空");
            }
            RectificationInfo rectificationInfo1 = rectificationInfoMapper.selectById(id);
            if (rectificationInfo1 == null) {
                return Answer.error("项目为空");
            }
            List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(
                    new LambdaQueryWrapper<DeadlineNode>()
                            .eq(DeadlineNode::getRectificationId, id)
                            .orderByAsc(DeadlineNode::getSort)
            );
            deadlineNodeList = filiterNode(rectificationInfo1, deadlineNodeList);
            List<DeadlineRectificationPeriod> deadlineRectificationPeriods = deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<DeadlineRectificationPeriod>()
                    .eq(DeadlineRectificationPeriod::getIsApproval, 1).orderByDesc(DeadlineRectificationPeriod::getId));
            Map<Integer, List<DeadlineRectificationPeriod>> deadlineRectificationPeriodMap = new HashMap<>();
            if (deadlineRectificationPeriods != null) {
                deadlineRectificationPeriodMap = deadlineRectificationPeriods.stream().filter(o -> o.getDeadlineNodeId() != null).collect(Collectors.groupingBy(DeadlineRectificationPeriod::getDeadlineNodeId));
            }
            List<DeadlineNodeUpdate> deadlineNodeUpdateList = deadlineNodeUpdateMapper.selectList(new LambdaQueryWrapper<DeadlineNodeUpdate>().eq(DeadlineNodeUpdate::getSignUser, rectificationInfo.getSignUser()));
            Map<String, List<DeadlineNodeUpdate>> deadlineNodeUpdateListMap = new HashMap<>();
            if (deadlineNodeUpdateList != null) {
                deadlineNodeUpdateListMap = deadlineNodeUpdateList.stream().filter(o -> o.getNodeId() != null).collect(Collectors.groupingBy(DeadlineNodeUpdate::getNodeId));
            }

            List<DeadlineNodeUpdate> list = deadlineNodeUpdateMapper.selectList(new LambdaQueryWrapper<DeadlineNodeUpdate>().isNull(DeadlineNodeUpdate::getApprovalStatus)
                    .orderByDesc(DeadlineNodeUpdate::getId));
            Map<String, List<DeadlineNodeUpdate>> listMap = new HashMap<>();
            if (list != null) {
                listMap = list.stream().filter(o -> o.getNodeId() != null).collect(Collectors.groupingBy(DeadlineNodeUpdate::getNodeId));
            }


            for (DeadlineNode node : deadlineNodeList) {
                List<DeadlineRectificationPeriod> deadlineRectificationPeriodList = deadlineRectificationPeriodMap.get(node.getId());
                List<DeadlineNodeUpdate> deadlineNodeUpdates = listMap.get(node.getId().toString());
                if (deadlineNodeUpdates != null && !deadlineNodeUpdates.isEmpty()) {
                    node.setIsHaveApproval(1);
                    node.setDeadlineNodeUpdate(deadlineNodeUpdates);
                }
                List<DeadlineNodeUpdate> deadlineNodeUpdate = deadlineNodeUpdateListMap.get(node.getId().toString());
                if (deadlineNodeUpdate != null && !deadlineNodeUpdate.isEmpty()) {
                    node.setIsPersonalApplication(deadlineNodeUpdate.size());
                }
                if (deadlineRectificationPeriodList != null) {
                    // 排序并过滤后的列表
                    List<DeadlineRectificationPeriod> sortedList = deadlineRectificationPeriodList.stream()
                            .filter(item -> item.getRectificationTime() != null)
                            .sorted(Comparator.comparing(DeadlineRectificationPeriod::getId,
                                    Comparator.reverseOrder()))
                            .collect(Collectors.toList());
                    // 先判断列表是否有元素，再获取第一个
                    if (!sortedList.isEmpty()) {
                        node.setDeadlineRectificationPeriod(sortedList.get(0).getRectificationTime());
                        node.setRectificationFrequency(sortedList.size());
                    }
                }
                DeadlineRectificationPeriod deadlineRectificationPeriod = deadlineRectificationPeriodMapper.selectOne(new LambdaQueryWrapper<DeadlineRectificationPeriod>()
                        .eq(DeadlineRectificationPeriod::getDeadlineNodeId, node.getId()).eq(DeadlineRectificationPeriod::getIsApproval, 0));
                if (deadlineRectificationPeriod != null) {
                    node.setIsHavePeriod(1);
                    node.setPeriodId(deadlineRectificationPeriod.getId());
                } else {
                    node.setIsHavePeriod(0);
                }
                if (node.getEndTime() != null) {
                    node.setIsEnd("1");
                } else {
                    node.setIsEnd("0");
                }
            }
            // 处理文件URL信息
            List<Map<String, Object>> resultList = deadlineNodeList.stream()
                    .map(node -> {
                        Map<String, Object> nodeMap = BeanUtil.beanToMap(node);
                        Integer nodeId = node.getId();
                        // 查询最新的记录
                        LambdaQueryWrapper<DeadLineNodeRecord> recordQuery = new LambdaQueryWrapper<>();
                        recordQuery.eq(DeadLineNodeRecord::getNodeId, nodeId)
                                .orderByDesc(DeadLineNodeRecord::getUpdateTime)
                                .orderByDesc(DeadLineNodeRecord::getId)
                                .last("LIMIT 1");

                        DeadLineNodeRecord latestRecord = deadLineNodeRecordMapper.selectOne(recordQuery);

                        // 处理附件信息
                        String fileUrls = "";
                        int attachmentCount = 0;
                        List<String> attachmentUrls = new ArrayList<>();

                        if (latestRecord != null) {
                            String dbFileUrl = latestRecord.getFileUrl();
                            if (StrUtil.isNotBlank(dbFileUrl)) {
                                // 处理可能的空字符串分割问题
                                String[] urls = dbFileUrl.split(",");
                                attachmentUrls = Arrays.stream(urls)
                                        .filter(StrUtil::isNotBlank)
                                        .collect(Collectors.toList());
                                attachmentCount = attachmentUrls.size();
                                fileUrls = String.join(",", attachmentUrls);
                            }
                        }
                        nodeMap.put("deadlineRectificationPeriod", node.getDeadlineRectificationPeriod());
                        nodeMap.put("rectificationFrequency", node.getRectificationFrequency());
                        nodeMap.put("fileUrls", fileUrls);
                        nodeMap.put("attachmentCount", attachmentCount);
                        nodeMap.put("attachmentUrls", attachmentUrls);
                        return nodeMap;
                    })
                    .collect(Collectors.toList());

            // 加判断条件 - 特殊排序处理
            if (rectificationInfo1.getProjectChildType() != null) {
                if (rectificationInfo1.getProjectChildType().contains("主城崛起") ||
                        rectificationInfo1.getProjectChildType().equals("项目建设")) {
                    if (!resultList.isEmpty()) {
                        // 寻找三个节点的位置
                        int firstDesignIndex = -1;
                        int huanIndex = -1;
                        int anIndex = -1;

                        for (int i = 0; i < resultList.size(); i++) {
                            Map<String, Object> nodeMap = resultList.get(i);
                            String nodeName = (String) nodeMap.get("name");
                            if ("完成初步设计批复".equals(nodeName)) {
                                firstDesignIndex = i;
                            } else if ("完成环评批复".equals(nodeName)) {
                                huanIndex = i;
                            } else if ("完成安评手续".equals(nodeName)) {
                                anIndex = i;
                            }
                        }

                        // 只有当三个节点都存在时才进行调整
                        if (firstDesignIndex != -1 && huanIndex != -1 && anIndex != -1) {
                            // 保存三个节点的引用
                            Map<String, Object> firstNode = resultList.get(firstDesignIndex);
                            Map<String, Object> huanNode = resultList.get(huanIndex);
                            Map<String, Object> anNode = resultList.get(anIndex);

                            // 创建新列表，先添加所有节点
                            List<Map<String, Object>> tempList = new ArrayList<>();
                            for (Map<String, Object> node : resultList) {
                                // 跳过需要重新排序的三个节点
                                if (!node.equals(firstNode) && !node.equals(huanNode) && !node.equals(anNode)) {

                                    tempList.add(node);
                                }
                            }

                            // 找到原列表中三个节点最早出现的位置
                            int insertPos = Math.min(firstDesignIndex, Math.min(huanIndex, anIndex));

                            // 插入调整后的节点顺序：初步设计 -> 环评 -> 安评
                            tempList.add(insertPos, firstNode);
                            tempList.add(insertPos + 1, huanNode);
                            tempList.add(insertPos + 2, anNode);

                            // 更新结果列表
                            resultList.clear();
                            resultList.addAll(tempList);
                        }
                    }
                }
            }

            return Answer.ok(resultList);
        } catch (Exception e) {
            e.printStackTrace();
            return Answer.error("查询失败");
        }
    }

    @Override
    public Answer<Object> queryDistributeDeadlineNodeList(RectificationInfo rectificationInfo) {
        try {
            JSONArray resList = new JSONArray();
            Integer id = rectificationInfo.getId();
            if (id == null) {
                return Answer.error("id不能为空");
            }
            RectificationInfo rectificationInfoRes = rectificationInfoMapper.selectById(id);
            if (rectificationInfoRes == null) {
                return Answer.error("id不存在");
            }
            Integer task2Id = rectificationInfoRes.getTask2Id();
            if (task2Id == null) {
                return Answer.error("task2Id不存在");
            }

            List<TicketProblemInfo> ticketProblemInfoList = ticketProblemInfoMapper.selectList(
                    new LambdaQueryWrapper<TicketProblemInfo>().eq(TicketProblemInfo::getTask2Id, task2Id));

            if (ticketProblemInfoList == null) {
                return Answer.error("问题清单不存在");
            }

            List<DeadlineRectificationPeriod> deadlineRectificationPeriods = deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<>());
            Map<Integer, List<DeadlineRectificationPeriod>> deadlineRectificationPeriodMap = new HashMap<>();
            if (deadlineRectificationPeriods != null) {
                deadlineRectificationPeriodMap = deadlineRectificationPeriods.stream().filter(o -> o.getDeadlineNodeId() != null).collect(Collectors.groupingBy(DeadlineRectificationPeriod::getDeadlineNodeId));
            }

            List<DeadlineNodeUpdate> deadlineNodeUpdateList = deadlineNodeUpdateMapper.selectList(new LambdaQueryWrapper<DeadlineNodeUpdate>().eq(DeadlineNodeUpdate::getSignUser, rectificationInfo.getSignUser()));
            Map<String, List<DeadlineNodeUpdate>> deadlineNodeUpdateListMap = new HashMap<>();
            if (deadlineNodeUpdateList != null) {
                deadlineNodeUpdateListMap = deadlineNodeUpdateList.stream().filter(o -> o.getNodeId() != null).collect(Collectors.groupingBy(DeadlineNodeUpdate::getNodeId));
            }

            List<DeadlineNodeUpdate> list = deadlineNodeUpdateMapper.selectList(new LambdaQueryWrapper<DeadlineNodeUpdate>().isNull(DeadlineNodeUpdate::getApprovalStatus)
                    .orderByDesc(DeadlineNodeUpdate::getId));
            Map<String, List<DeadlineNodeUpdate>> listMap = new HashMap<>();
            if (list != null) {
                listMap = list.stream().filter(o -> o.getNodeId() != null).collect(Collectors.groupingBy(DeadlineNodeUpdate::getNodeId));
            }


            List<String> departmentNameList = ticketProblemInfoList.stream()
                    .map(TicketProblemInfo::getProcessHandlerDepartment)
                    .distinct()
                    .filter(StrUtil::isNotBlank)
                    .collect(Collectors.toList());

            if (CollectionUtil.isEmpty(departmentNameList)) {
                return Answer.error("处理部门不存在");
            }

            for (String departmentName : departmentNameList) {
                JSONObject resJson = new JSONObject();
                List<TicketProblemInfo> collect = ticketProblemInfoList.stream()
                        .filter(ticketProblemInfo -> departmentName.equals(ticketProblemInfo.getProcessHandlerDepartment()))
                        .collect(Collectors.toList());

                Integer ticketId = collect.get(0).getId();

                // 查询截止节点列表
                List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(
                        new LambdaQueryWrapper<DeadlineNode>().eq(DeadlineNode::getMainId, ticketId));
                out.println(deadlineNodeList);
                for (DeadlineNode node : deadlineNodeList) {
                    List<DeadlineNodeUpdate> deadlineNodeUpdates = listMap.get(node.getId().toString());
                    if (deadlineNodeUpdates != null) {
                        node.setIsHaveApproval(1);
                        node.setDeadlineNodeUpdate(deadlineNodeUpdates);
                    }
                    List<DeadlineNodeUpdate> deadlineNodeUpdate = deadlineNodeUpdateListMap.get(node.getId().toString());
                    if (deadlineNodeUpdate != null) {
                        node.setIsPersonalApplication(deadlineNodeUpdate.size());
                    }

                    List<DeadlineRectificationPeriod> deadlineRectificationPeriodList = deadlineRectificationPeriodMap.get(node.getId());
                    if (deadlineRectificationPeriodList != null) {
                        // 排序并过滤后的列表
                        List<DeadlineRectificationPeriod> sortedList = deadlineRectificationPeriodList.stream()
                                .filter(item -> item.getRectificationTime() != null)
                                .sorted(Comparator.comparing(DeadlineRectificationPeriod::getRectificationTime,
                                        Comparator.reverseOrder()))
                                .collect(Collectors.toList());
                        // 先判断列表是否有元素，再获取第一个
                        if (!sortedList.isEmpty()) {
                            node.setDeadlineRectificationPeriod(sortedList.get(0).getRectificationTime());
                            node.setRectificationFrequency(sortedList.size());
                        }
                    }
                }
                // 处理文件URL信息
                List<Map<String, Object>> processedNodeList =
                        deadlineNodeList.stream()
                                .map(node -> {
                                    Map<String, Object> nodeMap = BeanUtil.beanToMap(node);
                                    Integer nodeId = node.getId();
                                    // 查询最新的记录
                                    LambdaQueryWrapper<DeadLineNodeRecord> recordQuery = new LambdaQueryWrapper<>();
                                    recordQuery.eq(DeadLineNodeRecord::getNodeId, nodeId)
                                            .orderByDesc(DeadLineNodeRecord::getUpdateTime)
                                            .orderByDesc(DeadLineNodeRecord::getId)
                                            .last("LIMIT 1");

                                    DeadLineNodeRecord latestRecord = deadLineNodeRecordMapper.selectOne(recordQuery);
                                    LOG.info("节点ID:{} 对应的最新记录：{}", nodeId, latestRecord != null ? latestRecord.getId() : "无记录");

                                    // 处理附件信息
                                    String fileUrls = "";
                                    int attachmentCount = 0;
                                    List<String> attachmentUrls = new ArrayList<>();

                                    if (latestRecord != null) {
                                        String dbFileUrl = latestRecord.getFileUrl();
                                        LOG.info("节点ID:{} 的fileUrl：{}", nodeId, dbFileUrl);
                                        if (StrUtil.isNotBlank(dbFileUrl)) {
                                            // 处理可能的空字符串分割问题
                                            String[] urls = dbFileUrl.split(",");
                                            attachmentUrls = Arrays.stream(urls)
                                                    .filter(StrUtil::isNotBlank)
                                                    .collect(Collectors.toList());
                                            attachmentCount = attachmentUrls.size();
                                            fileUrls = String.join(",", attachmentUrls);
                                        }
                                    }

                                    nodeMap.put("fileUrls", fileUrls);
                                    nodeMap.put("attachmentCount", attachmentCount);
                                    nodeMap.put("attachmentUrls", attachmentUrls);
                                    return nodeMap;
                                })
                                .collect(Collectors.toList());
                resJson.put("departmentName", departmentName);
                resJson.put("deadlineNodeList", processedNodeList);
                resList.add(resJson);
            }

            return Answer.ok(resList);
        } catch (Exception e) {
            e.printStackTrace();
            return Answer.error("查询失败");
        }
    }


    @Override
    public Answer<Object> issueConfirmation(RectificationInfo rectificationInfo) {
        try {
            Integer id = rectificationInfo.getId();
            if (id == null) {
                return Answer.error("id不能为空");
            }
            RectificationInfo rectificationInfoRes = rectificationInfoMapper.selectById(id);
            if (rectificationInfoRes == null) {
                return Answer.error("id不存在");
            }
            Integer task2Id = rectificationInfoRes.getTask2Id();
            if (task2Id == null) {
                return Answer.error("task2Id不存在");
            }
            List<TicketProblemInfo> ticketProblemInfoList = ticketProblemInfoMapper.selectList(new LambdaQueryWrapper<TicketProblemInfo>().eq(TicketProblemInfo::getTask2Id, task2Id).eq(TicketProblemInfo::getStatus, "0"));
            if (ticketProblemInfoList == null) {
                return Answer.error("问题清单不存在");
            }
            ticketProblemInfoMapper.update(new TicketProblemInfo(), new LambdaUpdateWrapper<TicketProblemInfo>().eq(TicketProblemInfo::getTask2Id, task2Id).set(TicketProblemInfo::getStatus, "1"));
            return Answer.ok("确认成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Answer.error("确认失败");
        }
    }

    @Override
    public Answer<Object> queryRectificationList(RectificationInfo rectificationInfo) {
        try {
            LambdaQueryWrapper<RectificationInfo> rectificationInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
            String state = rectificationInfo.getState();
            String projectType = rectificationInfo.getProjectType();
            String areaName = rectificationInfo.getAreaName();
            String leadUnit = rectificationInfo.getLeadUnit();
            String leadLeader = rectificationInfo.getLeadLeader();
            String personLiaible = rectificationInfo.getPersonLiaible();
            String title = rectificationInfo.getTitle();
            String departmentName = rectificationInfo.getDepartmentName();
            String projectChildType = rectificationInfo.getProjectChildType();
            String projectChildCategoryType = rectificationInfo.getProjectChildCategoryType();
            if (StrUtil.isNotBlank(state)) {
                rectificationInfoLambdaQueryWrapper.eq(RectificationInfo::getState, state);
            }
            if (StrUtil.isNotBlank(projectType)) {
                rectificationInfoLambdaQueryWrapper.eq(RectificationInfo::getProjectType, projectType);
            }
            if (StrUtil.isNotBlank(areaName)) {
                rectificationInfoLambdaQueryWrapper.eq(RectificationInfo::getAreaName, areaName);
            }
            if (StrUtil.isNotBlank(leadUnit)) {
                rectificationInfoLambdaQueryWrapper.eq(RectificationInfo::getLeadUnit, leadUnit);
            }
            if (StrUtil.isNotBlank(leadLeader)) {
                rectificationInfoLambdaQueryWrapper.eq(RectificationInfo::getLeadLeader, leadLeader);
            }
            if (StrUtil.isNotBlank(personLiaible)) {
                rectificationInfoLambdaQueryWrapper.eq(RectificationInfo::getPersonLiaible, personLiaible);
            }
            if (StrUtil.isNotBlank(title)) {
                rectificationInfoLambdaQueryWrapper.eq(RectificationInfo::getTitle, title);
            }
            if (StrUtil.isNotBlank(departmentName)) {
                rectificationInfoLambdaQueryWrapper.eq(RectificationInfo::getDepartmentName, departmentName);
            }
            if (StrUtil.isNotBlank(projectChildType)) {
                rectificationInfoLambdaQueryWrapper.eq(RectificationInfo::getProjectChildType, projectChildType);
            }
            if (StrUtil.isNotBlank(projectChildCategoryType)) {
                rectificationInfoLambdaQueryWrapper.eq(RectificationInfo::getProjectChildCategoryType, projectChildCategoryType);
            }
            rectificationInfoLambdaQueryWrapper.orderByDesc(RectificationInfo::getId);
            List<RectificationInfo> rectificationInfos = rectificationInfoMapper.selectList(rectificationInfoLambdaQueryWrapper);
            if (CollectionUtil.isNotEmpty(rectificationInfos)) {
                rectificationInfos.parallelStream().forEach(rectificationInfo1 -> {
                    List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(new LambdaQueryWrapper<DeadlineNode>().eq(DeadlineNode::getMainId, rectificationInfo1.getId())
                            .eq(DeadlineNode::getTypeName, "整改").orderByDesc(DeadlineNode::getId));
                    rectificationInfo1.setDeadlineNodeList(deadlineNodeList);
                });
            }
            return Answer.ok(rectificationInfos);
        } catch (Exception e) {
            e.printStackTrace();
            return Answer.error("查询失败");
        }
    }

    public Integer getSummaryId(Integer task2id) {
        if (task2id == null) {
            return null;
        }
        Task2Info task2Info = task2InfoMapper.selectById(task2id);
        if (task2Info != null) {
            Integer task1Id = task2Info.getTask1Id();
            if (task1Id != null) {
                Task1Info task1Info = task1InfoMapper.selectById(task1Id);
                if (task1Info != null) {
                    Integer taskId = task1Info.getTaskId();
                    if (taskId != null) {
                        TaskInfo taskInfo = taskInfoMapper.selectById(taskId);
                        if (taskInfo != null) {
                            return taskInfo.getSummaryId();
                        }
                    }
                }
            }
        }
        return null;
    }

    @Override
    public Answer<Object> queryDirTreeByType(String type) {

        if ("type".equals(type)) {
            List<RectificationInfoDirTreeVo> resultList = rectificationInfoMapper.selectProjectDirTree();
            for (RectificationInfoDirTreeVo rootNode : resultList) {
                if (rootNode.getName().equals("高质量发展")) {
                    if (rootNode.getChild() != null) {
                        for (int i = 0; i < rootNode.getChild().size(); i++) {
                            if (rootNode.getChild().get(i).getName().equals("服务企业")) {
                                List<RectificationInfoDirTreeVo> childList = new ArrayList<>();
                                RectificationInfoDirTreeVo child1List1 = new RectificationInfoDirTreeVo();
                                child1List1.setName("重点工业企业包保服务");
                                child1List1.setCount(0);
                                RectificationInfoDirTreeVo child1List2 = new RectificationInfoDirTreeVo();
                                child1List2.setName("服务业市场主体培育");
                                child1List2.setCount(0);
                                rootNode.getChild().get(i).getChild().add(child1List1);
                                rootNode.getChild().get(i).getChild().add(child1List2);
                            }
                        }
                    }
//                   RectificationInfoDirTreeVo rootNo = new RectificationInfoDirTreeVo();
//                   rootNo.setName("服务企业");
//                   //重点工业企业包保服务
//                   List<RectificationInfoDirTreeVo> childList = new ArrayList<>();
//                   RectificationInfoDirTreeVo child1List1 = new RectificationInfoDirTreeVo();
//                   child1List1.setName("重点工业企业包保服务");
//                   child1List1.setCount(0);
//                   RectificationInfoDirTreeVo child1List2 = new RectificationInfoDirTreeVo();
//                   child1List2.setName("服务业市场主体培育");
//                   child1List2.setCount(0);
//                   childList.add(child1List1);
//                   childList.add(child1List2);
//                   rootNo.setCount(0);
//                   rootNo.setChild(childList);
//                   rootNode.getChild().add(rootNo);
                }
            }
            // 根节点排序规则
            Map<String, Integer> ORDER_MAP = new HashMap<String, Integer>() {{
                put("高质量发展", 0);
                put("民生保障", 1);
                put("防风险促稳定", 2);
                put("学查改常态化作风建设", 3);
                put("其他重点事项", 4);
            }};

            // 对根节点进行排序
            resultList = resultList.stream()
                    .sorted((node1, node2) -> {
                        int priority1 = ORDER_MAP.getOrDefault(node1.getName(), Integer.MAX_VALUE);
                        int priority2 = ORDER_MAP.getOrDefault(node2.getName(), Integer.MAX_VALUE);
                        return Integer.compare(priority1, priority2);
                    })
                    .collect(Collectors.toList());

            // 子节点排序规则
            List<String> customOrder = Arrays.asList(
                    "项目建设",
                    "招商引资",
                    "服务企业",
                    "主城崛起-科技城",
                    "主城崛起-空港城",
                    "主城崛起-文旅城",
                    "省市十大民生项目滞后事项",
                    "信访积案化解",
                    "河湖长制",
                    "林长制",
                    "大气污染防治",
                    "打击治理电信网络诈骗",
                    "开展禁毒整治攻坚"
            );

            // 遍历根节点，处理每个节点的子节点排序
            for (RectificationInfoDirTreeVo rootNode : resultList) {
                List<RectificationInfoDirTreeVo> children = rootNode.getChild();
                if (children == null || children.isEmpty()) {
                    continue;
                }

                // 创建新的可修改列表进行排序
                List<RectificationInfoDirTreeVo> sortedChildren = new ArrayList<>(children);
                sortedChildren.sort((node1, node2) -> {
                    int priority1 = getPriority(node1.getName(), customOrder);
                    int priority2 = getPriority(node2.getName(), customOrder);
                    return Integer.compare(priority1, priority2);
                });

                // 将排序后的列表设置回节点
                rootNode.setChild(sortedChildren);

                // 如果有更深层次的子节点需要排序，可以在这里递归处理
                // sortGrandChildren(sortedChildren, customOrder);
            }

            return Answer.ok(resultList);
        } else if ("area".equals(type)) {
//            List<RectificationInfoDirTreeVo> resultList = rectificationInfoMapper.selectDepartmentDirTree();
//            RectificationInfoDirTreeVo rectificationInfoDirTreeVo = new RectificationInfoDirTreeVo();
//            rectificationInfoDirTreeVo.setName("市本级");
//            rectificationInfoDirTreeVo.setCount(0);
//            // 使用Map来合并相同名称的节点
//            Map<String, RectificationInfoDirTreeVo> nodeMap = new HashMap<>();
//            for (RectificationInfoDirTreeVo r : resultList) {
//                rectificationInfoDirTreeVo.setCount(rectificationInfoDirTreeVo.getCount() + r.getCount());
//                if (r.getChild() != null) {
//                    for (RectificationInfoDirTreeVo child : r.getChild()) {
//                        // 合并相同名称的节点
//                        if (nodeMap.containsKey(child.getName())) {
//                            RectificationInfoDirTreeVo existingNode = nodeMap.get(child.getName());
//                            existingNode.setCount(existingNode.getCount() + child.getCount());
//                            // 递归合并子节点
//                            mergeChildren(existingNode.getChild(), child.getChild());
//                        } else {
//                            nodeMap.put(child.getName(), child);
//                        }
//                    }
//                }
//            }
//            // 将Map中的值转换为列表
//            List<RectificationInfoDirTreeVo> mergedChildren = new ArrayList<>(nodeMap.values());
//            rectificationInfoDirTreeVo.setChild(mergedChildren);
            List<RectificationInfoDirTreeVo> rectificationInfoDirTreeVos = rectificationInfoMapper.selectDepartmentStreetNameList();
//            rectificationInfoDirTreeVos.add(rectificationInfoDirTreeVo);
            return Answer.ok(rectificationInfoDirTreeVos);
        } else if ("department".equals(type)) {
            List<RectificationInfoDirTreeVo> resultList = rectificationInfoMapper.selectDepartmentDirTree();
            return Answer.ok(resultList);
        } else {
            return Answer.error("无效的入参：" + type);
        }
    }

    //            rectificationInfoList.stream()
//                    .filter(item -> {
//                        Integer itemLevel = item.getWarnLevel();
//                        // 如果warnLevel为null，不做过滤（保留所有）
//                        if (warnLevel == null) {
//                            return true;
//                        }
//                        // 如果warnLevel不为null，则需要itemLevel不为null且与warnLevel相等
//                        return itemLevel != null && warnLevel.equals(itemLevel);
//                    }).collect(Collectors.toList());

    @Override
    public Answer<Object> groupByArea(String currentDepartment, Integer warnLevel, String zerenDepartment) {
        //查询关联到自己的所有 RectificationInfo 清单
        List<RectificationInfo> rectificationInfoList = fetchAllRectification(currentDepartment);
        if (StrUtil.isNotBlank(zerenDepartment)) {
            rectificationInfoList = rectificationInfoList.stream().
                    filter(item -> item.getDepartmentName().equals(zerenDepartment)).
                    collect(Collectors.toList());
        }
        if (warnLevel != null) {
//            List<DeadlineRectificationPeriod> deadlineRectificationPeriods = deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<>());
//            for (RectificationInfo rectificationInfoItem : rectificationInfoList) {
//                // ---------------------- 原有currentNode和warnLevel计算逻辑（不变）----------------------
//                List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(
//                        new LambdaQueryWrapper<DeadlineNode>()
//                                .eq(DeadlineNode::getRectificationId, rectificationInfoItem.getId())
//                                .orderByDesc(DeadlineNode::getId)
//                );
//                deadlineNodeList = filiterNode(rectificationInfoItem,deadlineNodeList);
//                RectificationInfo  rectificationInfoItem2 = calcWarnLevel(rectificationInfoItem,deadlineNodeList,deadlineRectificationPeriods);
//                rectificationInfoItem.setWarnLevel(rectificationInfoItem2.getWarnLevel());
//            }
            rectificationInfoList = rectificationInfoList.stream()
                    .filter(item -> item.getWarnLevel().equals(warnLevel)).collect(Collectors.toList());
        }
//        if (rectificationInfoList!=null){
//            System.out.println("rectificationInfoListSize: "+rectificationInfoList.size());
//            for (RectificationInfo rectificationInfoItem : rectificationInfoList) {
//                // 去查ticket表, 有单位就复制单位进去  // 这里还是要修改
//                List<TicketProblemInfo> ticketProblemInfoList = ticketProblemInfoMapper.selectList(
//                        new LambdaQueryWrapper<TicketProblemInfo>().eq(TicketProblemInfo::getTask2Id, rectificationInfoItem.getTask2Id()));
//                if (ticketProblemInfoList!=null && ticketProblemInfoList.size()>0){
//                    for (int i = 0; i < ticketProblemInfoList.size(); i++) {
//                        RectificationInfo newRectification = new RectificationInfo();
//                        BeanUtils.copyProperties(rectificationInfoItem, newRectification);
//                        newRectification.setDepartmentName(ticketProblemInfoList.get(i).getProcessHandlerDepartment());
//                        rectificationInfoList.add(newRectification);
//                    }
//                }
//            }
//            System.out.println("rectificationInfoListSize2: "+rectificationInfoList.size());
//        }
        List<TreeNode> newList = RectificationWithDepartmentClassifier.classifyByDepartment(rectificationInfoList);
        return Answer.ok(newList);
    }

    @Override
    public Answer<Object> groupByType(String currentDepartment, Integer warnLevel, String zerenDepartment) {
        //查询关联到自己的所有 RectificationInfo 清单
        List<RectificationInfo> rectificationInfoList = fetchAllRectification(currentDepartment);
        if (StrUtil.isNotBlank(zerenDepartment)) {
            rectificationInfoList = rectificationInfoList.stream().
                    filter(item -> item.getDepartmentName().equals(zerenDepartment)).
                    collect(Collectors.toList());
        }
        if (warnLevel != null) {
            rectificationInfoList = rectificationInfoList.stream()
                    .filter(item -> {
                        // 确保item.getWarnLevel()不为null，避免空指针
                        Integer itemLevel = item.getWarnLevel();
                        return itemLevel != null && warnLevel.equals(itemLevel);
                    })
                    .collect(Collectors.toList());
        }
        List<TreeNode> newList = RectificationClassifier.classifyThreeLevels(rectificationInfoList);
        return Answer.ok(newList);
    }

    @Override
    public Answer<Object> groupNodeDepartmentType(JSONObject reqJson) {

        String title = reqJson.getString("title");
        String state = reqJson.getString("state");
        String projectType = reqJson.getString("projectType");
        String areaName = reqJson.getString("areaName");
        String leadUnit = reqJson.getString("leadUnit");
        String label = reqJson.getString("label");
        String currentDepartment = reqJson.getString("departmentName"); //当前用户部门
        String projectChildType = reqJson.getString("projectChildType");

        String zerenDepartment = reqJson.getString("zerenDepartment");
        String nodeDepartment = reqJson.getString("nodeDepartment");
        String warnLevel = reqJson.getString("warnLevel");

        String personLiaible = reqJson.getString("personLiaible");
        String leadLeader = reqJson.getString("leadLeader");
        String projectChildCategoryType = reqJson.getString("projectChildCategoryType");
        JSONArray projectChildTypeList = reqJson.getJSONArray("projectChildTypeList");

        // 1. 先查询所有基础数据（暂不做前置过滤）
        List<RectificationInfo> recList = fetchAllRectification(currentDepartment);

        // 2. 前置条件过滤：增加“条件为空则不过滤”的判断，避免误伤
        // 项目类型过滤（为空则不过滤）
        if (StrUtil.isNotBlank(projectType)) {
            recList = recList.stream()
                    .filter(item -> item.getProjectType() != null && item.getProjectType().contains(projectType))
                    .collect(Collectors.toList());
        }
        // 项目子类型列表过滤（为空/无元素则不过滤）
        if (projectChildTypeList != null && !projectChildTypeList.isEmpty()) {
            recList = recList.stream()
                    .filter(item -> item.getProjectChildType() != null && projectChildTypeList.contains(item.getProjectChildType()))
                    .collect(Collectors.toList());
        }
        // 项目子类型过滤（为空则不过滤）
        if (StrUtil.isNotBlank(projectChildType)) {
            recList = recList.stream()
                    .filter(item -> item.getProjectChildType() != null && item.getProjectChildType().equals(projectChildType))
                    .collect(Collectors.toList());
        }
        // 项目子分类过滤（为空则不过滤）
        if (StrUtil.isNotBlank(projectChildCategoryType)) {
            recList = recList.stream()
                    .filter(item -> item.getProjectChildCategoryType() != null && item.getProjectChildCategoryType().contains(projectChildCategoryType))
                    .collect(Collectors.toList());
        }
        // 标题过滤（为空则不过滤）
        if (StrUtil.isNotBlank(title)) {
            recList = recList.stream()
                    .filter(item -> item.getTitle() != null && item.getTitle().contains(title))
                    .collect(Collectors.toList());
        }
        // 牵头领导过滤（为空则不过滤）
        if (StrUtil.isNotBlank(leadLeader)) {
            recList = recList.stream()
                    .filter(item -> item.getLeadLeader() != null && item.getLeadLeader().contains(leadLeader))
                    .collect(Collectors.toList());
        }
        // 负责人过滤（为空则不过滤，注意字段是petitioner，确认是否匹配需求）
        if (StrUtil.isNotBlank(personLiaible)) {
            recList = recList.stream()
                    .filter(item -> item.getPetitioner() != null && item.getPetitioner().contains(personLiaible))
                    .collect(Collectors.toList());
        }

        // 3. 部门关联过滤：修复漏判字段，确保creatorUnit能被正确匹配
        if (StrUtil.isNotBlank(currentDepartment) || StrUtil.isNotBlank(nodeDepartment)) {
            String targetNodeDept = StrUtil.isNotBlank(nodeDepartment) ? nodeDepartment : currentDepartment;
            String nodeDeptShort = targetNodeDept.replace("市发改委", "发改")
                    .replace("市资建局", "资建")
                    .replace("市林业局", "林业")
                    .replace("市财政局", "财政")
                    .replace("市生态环境局", "生态")
                    .replace("市应急管理局", "应急")
                    .replace("市自然资源规划局", "自规部门")
                    .replace("市住新局", "住建")
                    .replace("市公安局", "公安");

            List<Integer> relatedRectificationIds = getFilteredTaskIds(nodeDeptShort);

            // 县市区用户：添加该区域的环节项目ID（保留原有逻辑）
            if (COUNTY_LEVEL_DISTRICTS2.contains(currentDepartment)) {
                List<Integer> departList = getFilteredAreaTaskIds(currentDepartment);
                relatedRectificationIds.addAll(departList);
            }

            // 关键：判断当前用户所属层级（是否为县市区）
            boolean isCountyUser = COUNTY_LEVEL_DISTRICTS2.contains(currentDepartment);
            Predicate<RectificationInfo> predicate = null;

            // 分支1：传入nodeDepartment（按环节关联过滤）
            if (StrUtil.isNotBlank(nodeDepartment)) {
                predicate = item -> {
                    // 基础条件：项目在环节关联列表中（环节有本单位）
                    boolean isLinkRelated = relatedRectificationIds != null && relatedRectificationIds.contains(item.getId());
                    if (!isLinkRelated) {
                        return false;
                    }

                    // 判断项目是否属于县区级区域
                    boolean isCountyArea = COUNTY_LEVEL_DISTRICTS2.contains(item.getDepartmentName());
                    // 判断发起单位是否为当前市级部门
                    boolean isSelfCreated = item.getCreatorUnit() != null && item.getCreatorUnit().contains(currentDepartment);
//                    boolean isSelfCreated = currentDepartment.equals(StrUtil.trimToNull(item.getCreatorUnit()));
                    // 判断发起单位是否为县区级单位
                    boolean isCreatorCounty = item.getCreatorUnit() != null &&
                            COUNTY_LEVEL_DISTRICTS2.contains(item.getCreatorUnit());

                    // 权限规则：
                    // 1. 县市区用户：仅看本区域项目（因环节已关联，无需额外判断）
                    // 2. 市级用户：
                    //    - 本单位发起的项目：无论区域都通过
                    //    - 非本单位发起的项目：非县区项目 或 县区项目但发起单位不是县区级
                    if (isCountyUser) {
                        return currentDepartment.equals(item.getDepartmentName());
                    } else {
                        return isSelfCreated || (!isCountyArea && !isCreatorCounty);
                    }
                };
            }
            // 分支2：传入currentDepartment（按“直接关联 + 环节关联”过滤）
            else if (StrUtil.isNotBlank(currentDepartment)) {
                predicate = item -> {
                    // 1. 关联条件判断（核心：补充责任单位关联）
                    // 直接关联：创建单位是本单位
//                    boolean isSelfCreated = currentDepartment.equals(StrUtil.trimToNull(item.getCreatorUnit()));
                    boolean isSelfCreated = item.getCreatorUnit() != null && item.getCreatorUnit().contains(currentDepartment);
                    // 直接关联：牵头单位包含本单位
                    boolean isLeadRelated = item.getLeadUnit() != null && item.getLeadUnit().contains(currentDepartment);
                    // 直接关联：责任单位包含本区域（县市区用户核心需求）
                    boolean isDeptRelated = item.getDepartmentName() != null && item.getDepartmentName().contains(currentDepartment);
                    // 环节关联：项目在环节关联列表中
                    boolean isLinkRelated = relatedRectificationIds != null && relatedRectificationIds.contains(item.getId());

                    // 至少满足一个关联条件
                    if (!(isSelfCreated || isLeadRelated || isDeptRelated || isLinkRelated)) {
                        return false;
                    }

                    // 2. 区域属性判断
                    // 判断项目归属区域是否为县区级
                    boolean isCountyArea = COUNTY_LEVEL_DISTRICTS2.contains(item.getDepartmentName());
                    // 判断发起单位是否为县区级单位
                    boolean isCreatorCounty = item.getCreatorUnit() != null &&
                            COUNTY_LEVEL_DISTRICTS2.contains(item.getCreatorUnit());
                    // 判断当前用户是否为县市区用户
                    boolean isCountyUser2 = COUNTY_LEVEL_DISTRICTS.contains(currentDepartment);

                    // 3. 权限规则（核心修正）
                    if (isCountyUser2) {
                        // 县市区用户：只要关联条件满足（含责任单位包含本区域）就通过
                        // 无需额外区域限制，因为关联条件已包含“责任单位包含本区域”
                        return true;
                    } else {
                        // 市级用户规则不变：
                        // - 本单位发起的项目：无论区域都通过
                        // - 非本单位发起的项目：非县区项目 或 县区项目但发起单位不是县区级
                        return isSelfCreated || (!isCountyArea && !isCreatorCounty);
                    }
                };
            }

            if (predicate != null) {
                recList = recList.stream().filter(predicate).collect(Collectors.toList());
            }
        }

        // 4. 警告级别过滤（修复空值判断逻辑）
        if (CollectionUtil.isNotEmpty(recList) && StrUtil.isNotBlank(warnLevel)) {
            recList = recList.stream()
                    .filter(item -> {
                        Integer itemLevel = item.getWarnLevel();
                        return itemLevel != null && warnLevel.equals(itemLevel.toString()); // 注意：warnLevel是String，需转成字符串匹配
                    })
                    .collect(Collectors.toList());
        }

        // 调试：打印最终结果数量
        System.out.printf("最终保留项目数量：%d%n", recList.size());

        List<TreeNode> newList = RectificationClassifier.classifyThreeLevels(recList);
        return Answer.ok(newList);
    }


    @Override
    public Answer<Object> groupByDepartment(String currentDepartment, Integer warnLevel, String zerenDepartment) {
        //查询关联到自己的所有 RectificationInfo 清单
        List<RectificationInfo> rectificationInfoList = fetchAllRectification(currentDepartment);
        if (StrUtil.isNotBlank(zerenDepartment)) {
            rectificationInfoList = rectificationInfoList.stream().
                    filter(item -> item.getDepartmentName().equals(zerenDepartment)).
                    collect(Collectors.toList());
        }
        // 拆分
        rectificationInfoList = RectificationClassifier.process(rectificationInfoList);
        if (warnLevel != null) {
//            List<DeadlineRectificationPeriod> deadlineRectificationPeriods = deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<>());
//            for (RectificationInfo rectificationInfoItem : rectificationInfoList) {
//                // ---------------------- 原有currentNode和warnLevel计算逻辑（不变）----------------------
//                List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(
//                        new LambdaQueryWrapper<DeadlineNode>()
//                                .eq(DeadlineNode::getRectificationId, rectificationInfoItem.getId())
//                                .orderByDesc(DeadlineNode::getId)
//                );
//                deadlineNodeList = filiterNode(rectificationInfoItem,deadlineNodeList);
//                RectificationInfo  rectificationInfoItem2 = calcWarnLevel(rectificationInfoItem,deadlineNodeList,deadlineRectificationPeriods);
//                rectificationInfoItem.setWarnLevel(rectificationInfoItem2.getWarnLevel());
//            }
            rectificationInfoList = rectificationInfoList.stream()
                    .filter(item -> {
                        Integer itemLevel = item.getWarnLevel();
                        // 如果warnLevel为null，不做过滤（保留所有）
                        if (warnLevel == null) {
                            return true;
                        }
                        // 如果warnLevel不为null，则需要itemLevel不为null且与warnLevel相等
                        return itemLevel != null && warnLevel.equals(itemLevel);
                    })
                    .collect(Collectors.toList());
        }
        List<TreeNode> newList = RectificationByDepartmentClassifier.classifyByDepartment(rectificationInfoList);
        newList = newList.stream().filter(item -> item.getCount() != 0).collect(Collectors.toList());
        return Answer.ok(newList);
    }


//    private List<RectificationInfo> fetchAllRectification(String currentDepartment){
//        LambdaQueryWrapper<RectificationInfo> rectificationInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
//        rectificationInfoLambdaQueryWrapper.ne(RectificationInfo::getState, "已关闭");
//        if(StrUtil.isNotBlank(currentDepartment)){
//            List<Long> relatedRectificationIds = deadlineNodeMapper.selectObjs(
//                            new LambdaQueryWrapper<DeadlineNode>()
//                                    .select(DeadlineNode::getRectificationId) // 只查mainId，减少数据传输
//                                    .eq(DeadlineNode::getDepartmentName, currentDepartment) // 匹配DeadlineNode的部门
//                                    .groupBy(DeadlineNode::getRectificationId) // 去重，避免重复的mainId
//                    ).stream()
//                    .map(obj -> Convert.toLong(obj)) // 转换为RectificationInfo的id类型（假设为Long，根据实际调整）
//                    .collect(Collectors.toList());
//            rectificationInfoLambdaQueryWrapper.and(wrapper ->
//                    wrapper.like(RectificationInfo::getDepartmentName, "%" + currentDepartment + "%")
//                            .or()
//                            .eq(RectificationInfo::getCreatorUnit, currentDepartment)
//                            .or()
//                            .eq(RectificationInfo::getLeadUnit, currentDepartment)
//            );
//            // 2.3 若关联的mainId不为空，才加OR in条件（避免in()空集合导致SQL错误）
//            if (CollectionUtil.isNotEmpty(relatedRectificationIds)) {
//                rectificationInfoLambdaQueryWrapper.or()
//                        .in(RectificationInfo::getId, relatedRectificationIds);
//            }
//        }
//        rectificationInfoLambdaQueryWrapper.orderByAsc(RectificationInfo::getId);
//        List<RectificationInfo> rectificationList = rectificationInfoMapper.selectList(rectificationInfoLambdaQueryWrapper);
//        List<CompanyData> companyList = companyDataMapper.selectList(new LambdaQueryWrapper<>());
//        List<EnterpriseData> enterpriseDataList = enterpriseDataMapper.selectList(new LambdaQueryWrapper<>());
//        if(StrUtil.isNotBlank(currentDepartment)){
//            // 为了环节的部门功能
    ////            for (int i = 0; i < rectificationList.size(); i++) {
    ////                rectificationList.get(i).setDepartmentName(currentDepartment);
    ////            }
//            companyList = companyList.stream().filter(item->item.getDepartmentName().contains(currentDepartment)).collect(Collectors.toList());
//            enterpriseDataList = enterpriseDataList.stream().filter(item->item.getDepartmentName().contains(currentDepartment)).collect(Collectors.toList());
//        }
//        for (int i = 0; i < companyList.size(); i++) {
//            RectificationInfo rectificationV2Vo = new RectificationInfo();
//            rectificationV2Vo.setProjectType("高质量发展");
//            rectificationV2Vo.setProjectChildType("服务企业");
//            rectificationV2Vo.setProjectChildCategoryType("重点工业企业包保服务");
//            rectificationV2Vo.setTitle(companyList.get(i).getName());
//            rectificationV2Vo.setContent(companyList.get(i).getBusiness());
//            rectificationV2Vo.setProgressAndProblem(companyList.get(i).getProblem());
//            rectificationV2Vo.setDepartmentName(companyList.get(i).getDepartmentName());
//            rectificationV2Vo.setCurrentNode("推进中");
//            rectificationV2Vo.setAreaName(companyList.get(i).getDepartmentName());
//            rectificationV2Vo.setProgressAndProblem(companyList.get(i).getProblem());
//            rectificationV2Vo.setPersonLiaible(companyList.get(i).getPersonResponsible());
//            rectificationV2Vo.setIsNotCalc(true);
//            if (companyList.get(i).getLampStatus().equals("1")){
//                rectificationV2Vo.setWarnLevel(0);
//            }else if (companyList.get(i).getLampStatus().equals("2")){
//                rectificationV2Vo.setWarnLevel(2);
//            }else{
//                rectificationV2Vo.setWarnLevel(1);
//            }
//            rectificationList.add(rectificationV2Vo);
//        }
//        for (int i = 0; i < enterpriseDataList.size(); i++) {
//            RectificationInfo rectificationV2Vo = new RectificationInfo();
//            rectificationV2Vo.setProjectType("高质量发展");
//            rectificationV2Vo.setProjectChildType("服务企业");
//            rectificationV2Vo.setProjectChildCategoryType("服务业市场主体培育");
//            rectificationV2Vo.setTitle(enterpriseDataList.get(i).getBusiness());
//            rectificationV2Vo.setContent(enterpriseDataList.get(i).getBusiness());
//            rectificationV2Vo.setProgressAndProblem(enterpriseDataList.get(i).getProblem());
//            rectificationV2Vo.setDepartmentName(enterpriseDataList.get(i).getDepartmentName());
//            rectificationV2Vo.setCurrentNode("推进中");
//            rectificationV2Vo.setAreaName(enterpriseDataList.get(i).getDepartmentName());
//            rectificationV2Vo.setProgressAndProblem(enterpriseDataList.get(i).getProblem());
//            rectificationV2Vo.setPersonLiaible(enterpriseDataList.get(i).getPersonResponsible());
//            rectificationV2Vo.setIsNotCalc(true);
//            if (enterpriseDataList.get(i).getLampStatus().equals("1")){
//                rectificationV2Vo.setWarnLevel(0);
//            }else if (enterpriseDataList.get(i).getLampStatus().equals("3")){
//                rectificationV2Vo.setWarnLevel(2);
//            }else{
//                rectificationV2Vo.setWarnLevel(1);
//            }
//            rectificationList.add(rectificationV2Vo);
//        }
//        return rectificationList;
//    }

    private List<RectificationInfo> fetchAllRectification(String currentDepartment) {
        // 1. 并行执行三个数据查询
        ExecutorService executor = Executors.newFixedThreadPool(3);

        Future<List<RectificationInfo>> rectificationFuture = executor.submit(() -> {
            LambdaQueryWrapper<RectificationInfo> wrapper = new LambdaQueryWrapper<>();
            wrapper.ne(RectificationInfo::getState, "已关闭")
                    .ne(RectificationInfo::getState, "已销号")
                    .ne(RectificationInfo::getState, "已退出")
                    .orderByAsc(RectificationInfo::getId);
            return rectificationInfoMapper.selectList(wrapper);
        });

        LocalDate latestMonth = getLatestMonth();
        Future<List<CompanyData>> companyFuture = executor.submit(() -> {
            LambdaQueryWrapper<CompanyData> wrapper = new LambdaQueryWrapper<>();
            if (StrUtil.isNotBlank(currentDepartment) && !currentDepartment.equals("市经信局")) {
                wrapper.like(CompanyData::getDepartmentName, currentDepartment);
            }
            if (latestMonth != null) {
                wrapper.eq(CompanyData::getDataMonth, latestMonth);
            }
            return companyDataMapper.selectList(wrapper);
        });

        Future<List<EnterpriseData>> enterpriseFuture = executor.submit(() -> {
            LambdaQueryWrapper<EnterpriseData> wrapper = new LambdaQueryWrapper<>();
            if (StrUtil.isNotBlank(currentDepartment) && !currentDepartment.equals("市发改委")) {
                wrapper.like(EnterpriseData::getDepartmentName, currentDepartment);
            }
            return enterpriseDataMapper.selectList(wrapper);
        });

        // 2. 获取结果
        List<RectificationInfo> rectificationList = new ArrayList<>();
        List<CompanyData> companyList = new ArrayList<>();
        List<EnterpriseData> enterpriseDataList = new ArrayList<>();

        try {
            rectificationList = rectificationFuture.get();
            companyList = companyFuture.get();
            enterpriseDataList = enterpriseFuture.get();
        } catch (InterruptedException | ExecutionException e) {
            // 处理异常，记录日志等
            Thread.currentThread().interrupt();
        } finally {
            executor.shutdown();
        }
        // 将 rectificationList 里面的 amount 改成1位小数,amount 可能是null;
        // 0 也要改成 0.0
        rectificationList.forEach(rectification -> {
            String amount = rectification.getAmount();
            if (amount == null) {
                // 若 amount 为 null，根据需求可设置为 null 或 0.0（这里保持 null）
                // 若需要将 null 转为 0.0，可改为：rectification.setAmount(0.0);
            } else {
                // 格式化为1位小数（0 会转为 0.0，其他数字四舍五入保留1位）
                rectification.setAmount(DecimalFormatUtils.formatDecimal(amount, 2));
            }
        });

        // 3. 使用并行流处理数据转换
        List<RectificationInfo> companyRectifications = companyList.parallelStream()
                .map(company -> createRectificationFromCompany(company))
                .collect(Collectors.toList());

        List<RectificationInfo> enterpriseRectifications = enterpriseDataList.parallelStream()
                .map(enterprise -> createRectificationFromEnterprise(enterprise))
                .collect(Collectors.toList());

        // 4. 合并所有列表
        List<RectificationInfo> result = new ArrayList<>(rectificationList);
        result.addAll(companyRectifications);
        result.addAll(enterpriseRectifications);

        return result;
    }

    private LocalDate getLatestMonth() {
        // 查询最大的dataMonth（按月份倒序取第一条）
        LambdaQueryWrapper<CompanyData> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(CompanyData::getDataMonth)
                .orderByDesc(CompanyData::getDataMonth)
                .last("LIMIT 1"); // 只取最大的那个月份
        CompanyData latestData = companyDataMapper.selectOne(queryWrapper);
        return latestData != null ? latestData.getDataMonth() : null;
    }

    private List<RectificationInfo> fetchAllRectification1(String currentDepartment) {
        // 1. 并行执行三个数据查询
        ExecutorService executor = Executors.newFixedThreadPool(3);

        Future<List<RectificationInfo>> rectificationFuture = executor.submit(() -> {
            LambdaQueryWrapper<RectificationInfo> wrapper = new LambdaQueryWrapper<>();
            wrapper.orderByAsc(RectificationInfo::getId);
            return rectificationInfoMapper.selectList(wrapper);
        });

        // 2. 获取结果
        List<RectificationInfo> rectificationList = new ArrayList<>();
        try {
            rectificationList = rectificationFuture.get();
        } catch (InterruptedException | ExecutionException e) {
            // 处理异常，记录日志等
            Thread.currentThread().interrupt();
        } finally {
            executor.shutdown();
        }
        // 将 rectificationList 里面的 amount 改成1位小数,amount 可能是null;
        // 0 也要改成 0.0
        rectificationList.forEach(rectification -> {
            String amount = rectification.getAmount();
            if (amount == null) {
                // 若 amount 为 null，根据需求可设置为 null 或 0.0（这里保持 null）
                // 若需要将 null 转为 0.0，可改为：rectification.setAmount(0.0);
            } else {
                // 格式化为1位小数（0 会转为 0.0，其他数字四舍五入保留1位）
                rectification.setAmount(DecimalFormatUtils.formatDecimal(amount, 2));
            }
        });
        // 4. 合并所有列表
        List<RectificationInfo> result = new ArrayList<>(rectificationList);
        return result;
    }

    // 提取公司数据转换方法
    private RectificationInfo createRectificationFromCompany(CompanyData company) {
        RectificationInfo rectification = new RectificationInfo();
        rectification.setProjectType("高质量发展");
        rectification.setProjectChildType("服务企业");
        rectification.setProjectChildCategoryType("重点工业企业包保服务");
        rectification.setCreatorUnit("市经信局");
        rectification.setTitle(company.getName());
        rectification.setContent(company.getBusiness());
        rectification.setProgressAndProblem(company.getProblem());
        rectification.setDepartmentName(company.getDepartmentName());
        rectification.setCurrentNode("推进中");
        rectification.setState("进行中");
        rectification.setAreaName(company.getDepartmentName());
        rectification.setCreatorContactName("张锦泉");
        rectification.setId(20000000 + company.getId());
        rectification.setCreatorContactPhone("18271560951");
        rectification.setRemark(company.getRemark());
        rectification.setPersonLiaible(company.getPersonResponsible());
        rectification.setPower(company.getPower());
        rectification.setBusiness(company.getBusiness());
        rectification.setInvoicing(company.getInvoicing());
        rectification.setIsNotCalc(true);

        // 使用条件表达式简化
        String lampStatus = company.getLampStatus();
        if ("1".equals(lampStatus)) {
            rectification.setWarnLevel(0);
        } else if ("2".equals(lampStatus)) {
            rectification.setWarnLevel(2);
        } else {
            rectification.setWarnLevel(1);
        }

        return rectification;
    }

    // 提取企业数据转换方法
    private RectificationInfo createRectificationFromEnterprise(EnterpriseData enterprise) {
        RectificationInfo rectification = new RectificationInfo();
        rectification.setProjectType("高质量发展");
        rectification.setProjectChildType("服务企业");
        rectification.setProjectChildCategoryType("重点服务业企业培育");
        ;
        rectification.setCreatorUnit("市发改委");
        rectification.setTitle(enterprise.getBusiness());
        rectification.setContent(enterprise.getBusiness());
        rectification.setProgressAndProblem(enterprise.getProblem());
        rectification.setDepartmentName(enterprise.getDepartmentName());
        rectification.setCurrentNode("推进中");
        rectification.setAreaName(enterprise.getDepartmentName());
        rectification.setPersonLiaible(enterprise.getPersonResponsible());
        rectification.setIsNotCalc(true);
        rectification.setState("进行中");
        rectification.setId(10000000 + enterprise.getId());
        rectification.setCreatorContactName("金雷钧");
        rectification.setCreatorContactPhone("18064172185");
//        rectification.setLeaderContactName("金雷钧");
//        rectification.setLeaderContactPhone("18064172185");
        rectification.setBusiness(enterprise.getBusiness());
        rectification.setCompanyNum(enterprise.getCompanyNum());
        rectification.setTaskNum(enterprise.getTaskNum());
        // 使用条件表达式简化
        String lampStatus = enterprise.getLampStatus();
        if ("1".equals(lampStatus)) {
            rectification.setWarnLevel(0);
        } else if ("3".equals(lampStatus)) {
            rectification.setWarnLevel(2);
        } else {
            rectification.setWarnLevel(1);
        }

        return rectification;
    }

    private List<RectificationInfo> fetchAllDutyRectification(String currentDepartment) {
        LambdaQueryWrapper<RectificationInfo> rectificationInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        rectificationInfoLambdaQueryWrapper.ne(RectificationInfo::getState, "已关闭").ne(RectificationInfo::getState, "已销号")
                .ne(RectificationInfo::getState, "已退出");
        if (StrUtil.isNotBlank(currentDepartment)) {
            List<Long> relatedRectificationIds = deadlineNodeMapper.selectObjs(
                            new LambdaQueryWrapper<DeadlineNode>()
                                    .select(DeadlineNode::getRectificationId) // 只查mainId，减少数据传输
                                    .eq(DeadlineNode::getDepartmentName, currentDepartment) // 匹配DeadlineNode的部门
                                    .groupBy(DeadlineNode::getRectificationId) // 去重，避免重复的mainId
                    ).stream()
                    .map(obj -> Convert.toLong(obj)) // 转换为RectificationInfo的id类型（假设为Long，根据实际调整）
                    .collect(Collectors.toList());
            rectificationInfoLambdaQueryWrapper.and(wrapper ->
                    wrapper.like(RectificationInfo::getDepartmentName, "%" + currentDepartment + "%")
            );
            // 2.3 若关联的mainId不为空，才加OR in条件（避免in()空集合导致SQL错误）
            if (CollectionUtil.isNotEmpty(relatedRectificationIds)) {
                rectificationInfoLambdaQueryWrapper.or()
                        .in(RectificationInfo::getId, relatedRectificationIds);
            }
        }
        rectificationInfoLambdaQueryWrapper.orderByAsc(RectificationInfo::getId);
        List<RectificationInfo> rectificationList = rectificationInfoMapper.selectList(rectificationInfoLambdaQueryWrapper);
        List<CompanyData> companyList = companyDataMapper.selectList(new LambdaQueryWrapper<>());
        List<EnterpriseData> enterpriseDataList = enterpriseDataMapper.selectList(new LambdaQueryWrapper<>());
        if (StrUtil.isNotBlank(currentDepartment)) {
            // 为了环节的部门功能
//            for (int i = 0; i < rectificationList.size(); i++) {
//                rectificationList.get(i).setDepartmentName(currentDepartment);
//            }
            companyList = companyList.stream().filter(item -> item.getDepartmentName().contains(currentDepartment)).collect(Collectors.toList());
            enterpriseDataList = enterpriseDataList.stream().filter(item -> item.getDepartmentName().contains(currentDepartment)).collect(Collectors.toList());
        }
        for (int i = 0; i < companyList.size(); i++) {
            RectificationInfo rectificationV2Vo = new RectificationInfo();
            rectificationV2Vo.setProjectType("高质量发展");
            rectificationV2Vo.setProjectChildType("服务企业");
            rectificationV2Vo.setProjectChildCategoryType("重点工业企业包保服务");
            rectificationV2Vo.setTitle(companyList.get(i).getName());
            rectificationV2Vo.setContent(companyList.get(i).getBusiness());
            rectificationV2Vo.setProgressAndProblem(companyList.get(i).getProblem());
            rectificationV2Vo.setDepartmentName(companyList.get(i).getDepartmentName());
            rectificationV2Vo.setCurrentNode("推进中");
            rectificationV2Vo.setState("进行中");
            rectificationV2Vo.setAreaName(companyList.get(i).getDepartmentName());
            rectificationV2Vo.setProgressAndProblem(companyList.get(i).getProblem());
            rectificationV2Vo.setPersonLiaible(companyList.get(i).getPersonResponsible());
            rectificationV2Vo.setIsNotCalc(true);
            if (companyList.get(i).getLampStatus().equals("1")) {
                rectificationV2Vo.setWarnLevel(0);
            } else if (companyList.get(i).getLampStatus().equals("2")) {
                rectificationV2Vo.setWarnLevel(2);
            } else {
                rectificationV2Vo.setWarnLevel(1);
            }
            rectificationList.add(rectificationV2Vo);
        }
        for (int i = 0; i < enterpriseDataList.size(); i++) {
            RectificationInfo rectificationV2Vo = new RectificationInfo();
            rectificationV2Vo.setProjectType("高质量发展");
            rectificationV2Vo.setProjectChildType("服务企业");
            rectificationV2Vo.setProjectChildCategoryType("服务业市场主体培育");
            rectificationV2Vo.setTitle(enterpriseDataList.get(i).getBusiness());
            rectificationV2Vo.setContent(enterpriseDataList.get(i).getBusiness());
            rectificationV2Vo.setProgressAndProblem(enterpriseDataList.get(i).getProblem());
            rectificationV2Vo.setDepartmentName(enterpriseDataList.get(i).getDepartmentName());
            rectificationV2Vo.setCurrentNode("推进中");
            rectificationV2Vo.setState("进行中");
            rectificationV2Vo.setAreaName(enterpriseDataList.get(i).getDepartmentName());
            rectificationV2Vo.setProgressAndProblem(enterpriseDataList.get(i).getProblem());
            rectificationV2Vo.setPersonLiaible(enterpriseDataList.get(i).getPersonResponsible());
            rectificationV2Vo.setIsNotCalc(true);
            if (enterpriseDataList.get(i).getLampStatus().equals("1")) {
                rectificationV2Vo.setWarnLevel(0);
            } else if (enterpriseDataList.get(i).getLampStatus().equals("3")) {
                rectificationV2Vo.setWarnLevel(2);
            } else {
                rectificationV2Vo.setWarnLevel(1);
            }
            rectificationList.add(rectificationV2Vo);
        }
        return rectificationList;
    }


    // 辅助方法：计算节点名称的排序优先级
    private int getPriority(String name, List<String> customOrder) {
        if (name == null || name.trim().isEmpty()) {
            return Integer.MAX_VALUE; // 空名称放最后
        }
        // 检查是否完全匹配或前缀匹配（处理"主城崛起-xxx"这种情况）
        for (int i = 0; i < customOrder.size(); i++) {
            String orderItem = customOrder.get(i);
            if (name.equals(orderItem) || name.startsWith(orderItem + "-")) {
                return i;
            }
        }

        // 不在排序列表中的项放最后
        return Integer.MAX_VALUE;
    }


    // 递归合并子节点的方法
    private void mergeChildren(List<RectificationInfoDirTreeVo> existingChildren, List<RectificationInfoDirTreeVo> newChildren) {
        if (newChildren == null || newChildren.isEmpty()) {
            return;
        }

        if (existingChildren == null) {
            existingChildren = new ArrayList<>();
        }

        Map<String, RectificationInfoDirTreeVo> childMap = new HashMap<>();
        for (RectificationInfoDirTreeVo existingChild : existingChildren) {
            childMap.put(existingChild.getName(), existingChild);
        }

        for (RectificationInfoDirTreeVo newChild : newChildren) {
            if (childMap.containsKey(newChild.getName())) {
                RectificationInfoDirTreeVo existingChild = childMap.get(newChild.getName());
                existingChild.setCount(existingChild.getCount() + newChild.getCount());
                mergeChildren(existingChild.getChild(), newChild.getChild());
            } else {
                childMap.put(newChild.getName(), newChild);
                existingChildren.add(newChild);
            }
        }
    }

    @Override
    public Answer<Object> queryDirTreeType(String currentDepartment) {
        LambdaQueryWrapper<RectificationInfo> rectificationInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (StrUtil.isNotBlank(currentDepartment)) {
            // 2. 核心修改：currentDepartment条件（包含RectificationInfo的3个字段 + DeadlineNode的departmentName）
            // 2.1 先查询DeadlineNode中departmentName匹配的mainId（即关联的RectificationInfo的id），去重
            List<Long> relatedRectificationIds = deadlineNodeMapper.selectObjs(
                            new LambdaQueryWrapper<DeadlineNode>()
                                    .select(DeadlineNode::getMainId) // 只查mainId，减少数据传输
                                    .eq(DeadlineNode::getDepartmentName, currentDepartment) // 匹配DeadlineNode的部门
                                    .groupBy(DeadlineNode::getMainId) // 去重，避免重复的mainId
                    ).stream()
                    .map(obj -> Convert.toLong(obj)) // 转换为RectificationInfo的id类型（假设为Long，根据实际调整）
                    .collect(Collectors.toList());
            // 2.2 主查询条件：(departmentName匹配 OR creatorUnit匹配 OR leadUnit匹配) OR (id在关联的mainId中)
            // 用and()包裹前3个条件，确保逻辑优先级正确
            rectificationInfoLambdaQueryWrapper.and(wrapper ->
                    wrapper.eq(RectificationInfo::getDepartmentName, currentDepartment)
                            .or()
                            .eq(RectificationInfo::getCreatorUnit, currentDepartment)
                            .or()
                            .eq(RectificationInfo::getLeadUnit, currentDepartment)
            );
            // 2.3 若关联的mainId不为空，才加OR in条件（避免in()空集合导致SQL错误）
            if (CollectionUtil.isNotEmpty(relatedRectificationIds)) {
                rectificationInfoLambdaQueryWrapper.or()
                        .in(RectificationInfo::getId, relatedRectificationIds);
            }
        }
        rectificationInfoLambdaQueryWrapper.orderByDesc(RectificationInfo::getId);
        List<RectificationInfo> rectificationList = rectificationInfoMapper.selectList(rectificationInfoLambdaQueryWrapper);
        // 将这个数据转成多层的tree结构
        return Answer.ok(RectificationTreeConverter.convertToTree((rectificationList)));
    }

    @Override
    public Answer<Object> queryDirTreeByTypeAll(String type, String departmentName, String leadUnit) {
        List<RectificationInfoDirTreeVo> resultList =
                rectificationInfoMapper.selectProjectDirTreeAll(departmentName, leadUnit);
        // 这是一个树,根据结构进行筛选;

        return Answer.ok(resultList);
    }

    @Override
    public Answer<Object> queryRectificationPage(JSONObject reqJson) {
        Integer pageSize = reqJson.getInteger("pageSize");
        Integer pageNum = reqJson.getInteger("pageNum");
        String title = reqJson.getString("title");
        String state = reqJson.getString("state");
        String projectType = reqJson.getString("projectType");
        String areaName = reqJson.getString("areaName");
        String leadUnit = reqJson.getString("leadUnit");
        String label = reqJson.getString("label");
        String currentDepartment = reqJson.getString("currentDepartment"); //当前用户部门
        String departmentName = reqJson.getString("departmentName");
        String projectChildType = reqJson.getString("projectChildType");

        String zerenDepartment = reqJson.getString("zerenDepartment");
        String nodeDepartment = reqJson.getString("nodeDepartment");
        String warnLevel = reqJson.getString("warnLevel");

        String personLiaible = reqJson.getString("personLiaible");
        String leadLeader = reqJson.getString("leadLeader");
        String projectChildCategoryType = reqJson.getString("projectChildCategoryType");
        JSONArray projectChildTypeList = reqJson.getJSONArray("projectChildTypeList");
        List<RectificationInfo> recList = new ArrayList<>();
        if (StrUtil.isNotBlank(state)) {
            recList = fetchAllRectification1(currentDepartment);
        } else {
            recList = fetchAllRectification(currentDepartment);
        }
        List<SignReceipt> signReceipts = signReceiptMapper.selectList(new LambdaQueryWrapper<SignReceipt>().eq(SignReceipt::getReceiptDepartment, currentDepartment));
        Map<Integer, List<SignReceipt>> signReceiptMap = new HashMap<>();
        if(signReceipts!=null&&!signReceipts.isEmpty()){
            signReceiptMap = signReceipts.stream()
                    .collect(Collectors.groupingBy(SignReceipt::getRectificationId));
        }
        if (StrUtil.isNotBlank(departmentName)) {
            recList = recList.stream()
                    .filter(item -> item.getDepartmentName() != null &&
                            item.getDepartmentName().contains(departmentName))
                    .collect(Collectors.toList());
        }

        if (StrUtil.isNotBlank(state)) {
            recList = recList.stream()
                    .filter(item -> item.getState() != null &&
                            item.getState().equals(state))
                    .collect(Collectors.toList());
        }

        if (StrUtil.isNotBlank(label)) {
            recList = recList.stream()
                    .filter(item -> item.getLabel() != null &&
                            item.getLabel().contains(label))
                    .collect(Collectors.toList());
        }
        if (StrUtil.isNotBlank(projectType)) {
            recList = recList.stream()
                    .filter(item -> item.getProjectType() != null &&
                            item.getProjectType().contains(projectType))
                    .collect(Collectors.toList());
        }
        if (projectChildTypeList != null && projectChildTypeList.size() > 0) {
            recList = recList.stream()
                    .filter(item -> item.getProjectChildType() != null &&
                            projectChildTypeList.contains(item.getProjectChildType()))
                    .collect(Collectors.toList());
        }
        if (StrUtil.isNotBlank(projectChildType)) {
            recList = recList.stream()
                    .filter(item -> item.getProjectChildType() != null &&
                            item.getProjectChildType().equals(projectChildType))
                    .collect(Collectors.toList());
        }
        if (StrUtil.isNotBlank(projectChildCategoryType)) {
            recList = recList.stream()
                    .filter(item -> item.getProjectChildCategoryType() != null &&
                            item.getProjectChildCategoryType().equals(projectChildCategoryType))
                    .collect(Collectors.toList());
        }
        if (StrUtil.isNotBlank(title)) {
            recList = recList.stream()
                    .filter(item -> item.getTitle() != null &&
                            item.getTitle().contains(title))
                    .collect(Collectors.toList());
        }
        if (StrUtil.isNotBlank(leadLeader)) {
            recList = recList.stream()
                    .filter(item -> item.getLeadLeader() != null &&
                            item.getLeadLeader().contains(leadLeader))
                    .collect(Collectors.toList());
        }
        if (StrUtil.isNotBlank(personLiaible)) {
            recList = recList.stream()
                    .filter(item -> item.getPetitioner() != null &&
                            item.getPetitioner().contains(personLiaible))
                    .collect(Collectors.toList());
        }
        if (StrUtil.isNotBlank(currentDepartment) || StrUtil.isNotBlank(nodeDepartment)) {
            String targetNodeDept = StrUtil.isNotBlank(nodeDepartment) ? nodeDepartment : currentDepartment;
            String nodeDeptShort = targetNodeDept.replace("市发改委", "发改")
                    .replace("市资建局", "资建")
                    .replace("市林业局", "林业")
                    .replace("市财政局", "财政")
                    .replace("市生态环境局", "生态")
                    .replace("市应急管理局", "应急")
                    .replace("市自然资源规划局", "自规部门")
                    .replace("市住新局", "住建")
                    .replace("市公安局", "公安");

            List<Integer> relatedRectificationIds = getFilteredTaskIds(nodeDeptShort);

            // 县市区用户：添加该区域的环节项目ID（保留原有逻辑）
            if (COUNTY_LEVEL_DISTRICTS2.contains(currentDepartment)) {
                List<Integer> departList = getFilteredAreaTaskIds(currentDepartment);
                relatedRectificationIds.addAll(departList);
            }

            // 关键：判断当前用户所属层级（是否为县市区）
            boolean isCountyUser = COUNTY_LEVEL_DISTRICTS2.contains(currentDepartment);
            Predicate<RectificationInfo> predicate = null;

            // 分支1：传入nodeDepartment（按环节关联过滤）
            if (StrUtil.isNotBlank(nodeDepartment)) {
                predicate = item -> {
                    // 基础条件：项目在环节关联列表中（环节有本单位）
                    boolean isLinkRelated = relatedRectificationIds != null && relatedRectificationIds.contains(item.getId());
                    if (!isLinkRelated) {
                        return false;
                    }

                    // 判断项目是否属于县区级区域
                    boolean isCountyArea = COUNTY_LEVEL_DISTRICTS2.contains(item.getDepartmentName());
                    // 判断发起单位是否为当前市级部门
//                    boolean isSelfCreated = currentDepartment.equals(StrUtil.trimToNull(item.getCreatorUnit()));
                    boolean isSelfCreated = item.getCreatorUnit() != null && item.getCreatorUnit().contains(currentDepartment);
                    // 判断发起单位是否为县区级单位
                    boolean isCreatorCounty = item.getCreatorUnit() != null &&
                            COUNTY_LEVEL_DISTRICTS2.contains(item.getCreatorUnit());

                    // 权限规则：
                    // 1. 县市区用户：仅看本区域项目（因环节已关联，无需额外判断）
                    // 2. 市级用户：
                    //    - 本单位发起的项目：无论区域都通过
                    //    - 非本单位发起的项目：非县区项目 或 县区项目但发起单位不是县区级
                    if (isCountyUser) {
                        return currentDepartment.equals(item.getDepartmentName());
                    } else {
                        return isSelfCreated || (!isCountyArea && !isCreatorCounty);
                    }
                };
            }
            // 分支2：传入currentDepartment（按“直接关联 + 环节关联”过滤）
            else if (StrUtil.isNotBlank(currentDepartment)) {
                predicate = item -> {
                    // 1. 关联条件判断（核心：补充责任单位关联）
                    // 直接关联：创建单位是本单位
//                    boolean isSelfCreated = currentDepartment.equals(StrUtil.trimToNull(item.getCreatorUnit()));
                    boolean isSelfCreated = item.getCreatorUnit() != null && item.getCreatorUnit().contains(currentDepartment);
                    // 直接关联：牵头单位包含本单位
                    boolean isLeadRelated = item.getLeadUnit() != null && item.getLeadUnit().contains(currentDepartment);
                    // 直接关联：责任单位包含本区域（县市区用户核心需求）
                    boolean isDeptRelated = item.getDepartmentName() != null && item.getDepartmentName().contains(currentDepartment);
                    // 环节关联：项目在环节关联列表中
                    boolean isLinkRelated = relatedRectificationIds != null && relatedRectificationIds.contains(item.getId());

                    // 至少满足一个关联条件
                    if (!(isSelfCreated || isLeadRelated || isDeptRelated || isLinkRelated)) {
                        return false;
                    }

                    // 2. 区域属性判断
                    // 判断项目归属区域是否为县区级
                    boolean isCountyArea = COUNTY_LEVEL_DISTRICTS2.contains(item.getDepartmentName());
                    // 判断发起单位是否为县区级单位
                    boolean isCreatorCounty = item.getCreatorUnit() != null &&
                            COUNTY_LEVEL_DISTRICTS2.contains(item.getCreatorUnit());
                    // 判断当前用户是否为县市区用户
                    boolean isCountyUser2 = COUNTY_LEVEL_DISTRICTS.contains(currentDepartment);

                    // 3. 权限规则（核心修正）
                    if (isCountyUser2) {
                        // 县市区用户：只要关联条件满足（含责任单位包含本区域）就通过
                        // 无需额外区域限制，因为关联条件已包含“责任单位包含本区域”
                        return true;
                    } else {
                        // 市级用户规则不变：
                        // - 本单位发起的项目：无论区域都通过
                        // - 非本单位发起的项目：非县区项目 或 县区项目但发起单位不是县区级
                        return isSelfCreated || (!isCountyArea && !isCreatorCounty);
                    }
                };
            }

            if (predicate != null) {
                recList = recList.stream().filter(predicate).collect(Collectors.toList());
            }
        }

        // 4. 并行处理警告级别计算
        if (CollectionUtil.isNotEmpty(recList)) {
            List<DeadlineRectificationPeriod> deadlineRectificationPeriods = deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<>());
            // 1. 收集所有需要查询的rectificationId（假设id是Long类型，根据实际类型调整）
            List<Integer> rectificationIds = recList.stream()
                    .map(RectificationInfo::getId)
                    .distinct() // 去重，避免重复ID
                    .collect(Collectors.toList());

            // 2. 批量查询所有相关的DeadlineNode（一次性数据库请求）
            // 2. 批量查询所有相关的DeadlineNode
            List<DeadlineNode> allDeadlineNodes = Collections.emptyList();
            if (!rectificationIds.isEmpty()) {
                allDeadlineNodes = deadlineNodeMapper.selectList(
                        new LambdaQueryWrapper<DeadlineNode>()
                                .in(DeadlineNode::getRectificationId, rectificationIds)
                                .orderByDesc(DeadlineNode::getId)
                );
            }
            // 3. 按rectificationId分组，存入Map（key: rectificationId，value: 对应的DeadlineNode列表）
            Map<Integer, List<DeadlineNode>> deadlineNodeMap = allDeadlineNodes.stream()
                    .collect(Collectors.groupingBy(DeadlineNode::getRectificationId));

            // 4. 循环处理recList，从Map中获取数据，避免重复查询
            for (RectificationInfo rectificationInfoItem : recList) {
                String currentDepartment2 = calculateCurrentDepartment2(rectificationInfoItem, currentDepartment);

                // 从Map中获取对应的DeadlineNode列表
                List<DeadlineNode> deadlineNodeList = deadlineNodeMap.getOrDefault(
                        rectificationInfoItem.getId(),
                        Collections.emptyList()
                );
                // 根据currentDepartment2过滤
                if (StrUtil.isNotBlank(currentDepartment2)) {
                    deadlineNodeList = deadlineNodeList.stream()
                            .filter(node -> node.getDepartmentName() != null
                                    && node.getDepartmentName().contains(currentDepartment2))
                            .collect(Collectors.toList());
                }
                // 原有业务逻辑不变
                deadlineNodeList = filiterNode(rectificationInfoItem, deadlineNodeList);
                RectificationInfo rectificationInfoItem2 = calcWarnLevel(rectificationInfoItem, deadlineNodeList, deadlineRectificationPeriods);
                rectificationInfoItem.setWarnLevel(rectificationInfoItem2.getWarnLevel());
            }
            if (warnLevel != null) {
                recList = recList.stream()
                        .filter(item -> {
                            Integer itemLevel = item.getWarnLevel();
                            // 如果warnLevel为null，不做过滤（保留所有）
                            if (warnLevel == null) {
                                return true;
                            }
                            // 如果warnLevel不为null，则需要itemLevel不为null且与warnLevel相等
                            return itemLevel != null && warnLevel.equals(itemLevel);
                        })
                        .collect(Collectors.toList());
            }
        }
        if ("已销号".equals(state)||"已退出".equals(state)){
            recList = recList.stream()
                    .sorted(Comparator.comparing(
                            RectificationInfo::getLastOperationTime,
                            Comparator.nullsLast(Comparator.reverseOrder())
                    ))
                    .collect(Collectors.toList());
        }
        if (currentDepartment!=null&&!"".equals(currentDepartment)) {
            for (RectificationInfo rectification : recList) {
                Integer rectId = rectification.getId();
                List<SignReceipt> receipts = signReceiptMap.get(rectId);
                if (receipts != null && !receipts.isEmpty()) {
                    rectification.setState("进行中");
                }
            }
        }else {
            for (RectificationInfo rectification : recList) {
                rectification.setState("进行中");
            }
        }
        Page<RectificationInfo> rectificationInfos = new Page<>();
        rectificationInfos.setRecords(recList);
        rectificationInfos.setTotal(recList.size());
        return Answer.ok(rectificationInfos);
    }

    // 提取方法，提高可读性
    private String calculateCurrentDepartment2(RectificationInfo item, String currentDepartment) {
        if (item.getCreatorUnit() != null && currentDepartment != null
                && item.getCreatorUnit().contains(currentDepartment)) {
            return "";
        } else if (item.getDepartmentName() != null && currentDepartment != null
                && item.getDepartmentName().contains(currentDepartment)) {
            return "";
        } else if (currentDepartment != null && COUNTY_LEVEL_DISTRICTS.contains(currentDepartment)) {
            return currentDepartment;
        }
        return "";
    }

    // 提取的公共处理方法
    private Answer processRectificationList(List<RectificationInfo> rectificationList,
                                            Integer pageNum, Integer pageSize,
                                            List<DeadlineRectificationPeriod> deadlineRectificationPeriods) {
        if (CollectionUtil.isNotEmpty(rectificationList)) {
            // 批量获取所有相关的DeadlineNode数据
            List<Integer> rectificationIds = rectificationList.stream()
                    .map(RectificationInfo::getId)
                    .collect(Collectors.toList());

            Map<Integer, List<DeadlineNode>> nodeMap = Collections.emptyMap();
            if (CollectionUtil.isNotEmpty(rectificationIds)) {
                List<DeadlineNode> allNodes = deadlineNodeMapper.selectList(
                        new LambdaQueryWrapper<DeadlineNode>()
                                .in(DeadlineNode::getRectificationId, rectificationIds)
                                .orderByDesc(DeadlineNode::getId)
                );

                nodeMap = allNodes.stream()
                        .collect(Collectors.groupingBy(DeadlineNode::getRectificationId));
            }

            // 创建final引用用于Lambda表达式
            final Map<Integer, List<DeadlineNode>> finalNodeMap = nodeMap;
            final List<DeadlineRectificationPeriod> finalDeadlinePeriods = deadlineRectificationPeriods;

            // 使用并行流处理警告级别计算
            rectificationList = rectificationList.parallelStream()
                    .map(rectificationInfo1 -> {
                        List<DeadlineNode> deadlineNodeList = finalNodeMap.getOrDefault(
                                rectificationInfo1.getId(), new ArrayList<>());
                        deadlineNodeList = filiterNode(rectificationInfo1, deadlineNodeList);
                        RectificationInfo updatedInfo = calcWarnLevel(
                                rectificationInfo1, deadlineNodeList, finalDeadlinePeriods);

                        rectificationInfo1.setWarnLevel(updatedInfo.getWarnLevel());
                        rectificationInfo1.setCurrentNode(updatedInfo.getCurrentNode());
                        return rectificationInfo1;
                    })
                    .collect(Collectors.toList());
        }

        // 计算分页相关参数
        int totalCount = rectificationList.size();
        int startIndex = Math.max(0, (pageNum - 1) * pageSize);
        int endIndex = Math.min(startIndex + pageSize, totalCount);

        // 截取当前页数据
        List<RectificationInfo> currentPageData = startIndex >= totalCount ?
                new ArrayList<>() : rectificationList.subList(startIndex, endIndex);
        // 构建分页对象
        Page<RectificationInfo> resultPage = new Page<>(pageNum, pageSize);
        resultPage.setTotal(totalCount);
        resultPage.setRecords(currentPageData);
        return Answer.ok(resultPage);
    }


    @Override
    public Answer<Object> queryRectificationListV2(RectificationInfo rectificationInfo) {
        try {
            LambdaQueryWrapper<RectificationInfo> rectificationInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
            String state = rectificationInfo.getState();
            String projectType = rectificationInfo.getProjectType();
            String areaName = rectificationInfo.getAreaName();
            String leadUnit = rectificationInfo.getLeadUnit();
            String departmentName = rectificationInfo.getDepartmentName();
            String projectChildType = rectificationInfo.getProjectChildType();
            String projectChildCategoryType = rectificationInfo.getProjectChildCategoryType();
            String currentNode1 = rectificationInfo.getCurrentNode();
            Integer warnLevel = rectificationInfo.getWarnLevel();
            rectificationInfoLambdaQueryWrapper.ne(RectificationInfo::getState, "已关闭").ne(RectificationInfo::getState, "已销号")
                    .ne(RectificationInfo::getState, "已退出");
            if (StrUtil.isNotBlank(state)) {
                //待审核
                if (state.equals("待审核")) {
                    rectificationInfoLambdaQueryWrapper.like(RectificationInfo::getState, state).or().eq(RectificationInfo::getState, "待销号");
                } else {
                    rectificationInfoLambdaQueryWrapper.eq(RectificationInfo::getState, state);
                }
            }
            if (StrUtil.isNotBlank(projectType)) {
                rectificationInfoLambdaQueryWrapper.eq(RectificationInfo::getProjectType, projectType);
            }
            if (StrUtil.isNotBlank(areaName)) {
                rectificationInfoLambdaQueryWrapper.like(RectificationInfo::getAreaName, areaName);
            }
            if (StrUtil.isNotBlank(leadUnit)) {
                rectificationInfoLambdaQueryWrapper.like(RectificationInfo::getLeadUnit, leadUnit);
            }
            if (StrUtil.isNotBlank(departmentName)) {
                rectificationInfoLambdaQueryWrapper.like(RectificationInfo::getDepartmentName, departmentName);
            }
            if (StrUtil.isNotBlank(projectChildType)) {
                rectificationInfoLambdaQueryWrapper.eq(RectificationInfo::getProjectChildType, projectChildType);
            }
            if (StrUtil.isNotBlank(projectChildCategoryType)) {
                rectificationInfoLambdaQueryWrapper.eq(RectificationInfo::getProjectChildCategoryType, projectChildCategoryType);
            }

            if (StrUtil.isNotBlank(rectificationInfo.getTitle())) {
                rectificationInfoLambdaQueryWrapper.like(RectificationInfo::getTitle, rectificationInfo.getTitle());
            }
            if (StrUtil.isNotBlank(rectificationInfo.getPersonLiaible())) {
                rectificationInfoLambdaQueryWrapper.like(RectificationInfo::getPersonLiaible, rectificationInfo.getPersonLiaible());
            }
            if (StrUtil.isNotBlank(rectificationInfo.getLeadLeader())) {
                rectificationInfoLambdaQueryWrapper.like(RectificationInfo::getLeadLeader, rectificationInfo.getLeadLeader());
            }


            rectificationInfoLambdaQueryWrapper.orderByDesc(RectificationInfo::getId);
            List<RectificationInfo> rectificationInfos = rectificationInfoMapper.selectList(rectificationInfoLambdaQueryWrapper);
            int completationAndNotOverdue = 0;//正常完成
            int completationAndOverdue = 0;//超时完成
            int notCompletationAndNotOverdue = 0;//正常未完成(=正常推进)
            int notCompletationAndOverdue = 0;//超时未完成(=超期)
            if (CollectionUtil.isNotEmpty(rectificationInfos)) {
                List<DeadlineRectificationPeriod> deadlineRectificationPeriods = deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<>());

                // 1. 提取所有整改记录的ID
                List<Integer> rectificationIds = rectificationInfos.stream()
                        .map(RectificationInfo::getId)
                        .collect(Collectors.toList());

                // 2. 批量查询所有关联的DeadlineNode（一次数据库查询）
                List<DeadlineNode> allDeadlineNodes = deadlineNodeMapper.selectList(
                        new LambdaQueryWrapper<DeadlineNode>()
                                .in(DeadlineNode::getRectificationId, rectificationIds)
                                .orderByDesc(DeadlineNode::getId)
                );

                // 3. 建立rectificationId到DeadlineNode列表的映射（分组）
                Map<Integer, List<DeadlineNode>> deadlineNodeMap = allDeadlineNodes.stream()
                        .collect(Collectors.groupingBy(DeadlineNode::getRectificationId));
                for (int index = 0; index < rectificationInfos.size(); index++) {
                    RectificationInfo rectificationInfo1 = rectificationInfos.get(index);
                    // 4 // 从映射中获取当前整改记录的节点列表（不存在时返回空列表，避免NPE）
                    List<DeadlineNode> deadlineNodeList = deadlineNodeMap.getOrDefault(
                            rectificationInfo1.getId(),
                            Collections.emptyList()
                    );
                    deadlineNodeList = filiterNode(rectificationInfo1, deadlineNodeList);
                    rectificationInfo1 = calcWarnLevel(rectificationInfo1, deadlineNodeList, deadlineRectificationPeriods);
                    boolean completation = false;  // 是否全部已完成
                    boolean notOverdue = true;  // 是否没有任何超期节点（以 warnLevel=2 作为超期依据）
                    if (StrUtil.isEmpty(rectificationInfo1.getCurrentNode()) && deadlineNodeList.size() > 0) {
                        completation = true;
                    } else {
                        completation = false;
                        if (rectificationInfo1.getWarnLevel() != null && rectificationInfo1.getWarnLevel() != 2) {
                            notOverdue = true;
                        } else {
                            notOverdue = false;
                        }
                    }
                    String statusLabel;
                    if (completation && notOverdue) {
                        statusLabel = "正常完成";
                    } else if (completation) {
                        statusLabel = "超期完成";
                    } else if (notOverdue) {
                        statusLabel = "正常推进"; // = 正常未完成
                    } else {
                        statusLabel = "超期";     // = 超时未完成
                    }
                    if (completation && notOverdue) {
                        completationAndNotOverdue++;
                    } else if (completation && !notOverdue) {
                        completationAndOverdue++;
                    } else if (!completation && notOverdue) {
                        notCompletationAndNotOverdue++;
                    } else {
                        notCompletationAndOverdue++;
                    }
                    rectificationInfos.get(index).setWarnLevel(rectificationInfo1.getWarnLevel());
                    rectificationInfos.get(index).setCurrentNode(rectificationInfo1.getCurrentNode());
                    rectificationInfos.get(index).setStatusLabel(statusLabel);
                }
            }
            if (currentNode1 != null && StrUtil.isNotEmpty(currentNode1)) {
                rectificationInfos = rectificationInfos.stream()
                        .filter(r -> StrUtil.nullToDefault(r.getCurrentNode(), "").contains(currentNode1))
                        .collect(Collectors.toList());
            }
            if (warnLevel != null) {
                rectificationInfos = rectificationInfos.stream()
                        .filter(r -> (r.getWarnLevel() != null && r.getWarnLevel().equals(warnLevel)))
                        .collect(Collectors.toList());
            }
            RectificationListVo result = new RectificationListVo();
            result.setDataList(rectificationInfos);
            result.setCompletationAndNotOverdue(completationAndNotOverdue);
            result.setCompletationAndOverdue(completationAndOverdue);
            result.setNotCompletationAndNotOverdue(notCompletationAndNotOverdue);
            result.setNotCompletationAndOverdue(notCompletationAndOverdue);
            return Answer.ok(result);
        } catch (Exception e) {
            e.printStackTrace();
            return Answer.error("查询失败");
        }
    }

    /**
     * 过滤node
     *
     * @param rectificationInfo
     * @param deadlineNodeList
     * @return
     */
    private List<DeadlineNode> filiterNode(RectificationInfo rectificationInfo, List<DeadlineNode> deadlineNodeList) {
//        if (rectificationInfo.getId()!=301&&rectificationInfo.getId()!=765) {
//            // === 按你现有两套白名单过滤（不变）===
//            if (rectificationInfo.getProjectChildType() != null && rectificationInfo.getProjectChildCategoryType() != null) {
//                boolean needFilter = (
//                        (rectificationInfo.getProjectChildType().contains("主城崛起") &&
//                                rectificationInfo.getProjectChildCategoryType().contains("招商签约未开工重点项目")) ||
//                                ("招商引资".equals(rectificationInfo.getProjectChildType()) &&
//                                        "签约未开工重点项目".equals(rectificationInfo.getProjectChildCategoryType()))
//                );
//                if (needFilter && deadlineNodeList != null) {
//                    List<String> allowedLinks = Arrays.asList(
//                            "生成项目（签约）",
//                            "纳入上级规划/纳入年度投资计划/完成项目建议书批复，启动项目实施",
//                            "完成规划选址",
//                            "完成环评批复",
//                            "完成安评手续",
//                            "完成用地手续批复（用地、工程规划许可证）",
//                            "完成用林手续批复",
//                            "完成项目招投标（含公示）",
//                            "办理施工许可证并开工建设"
//                    );
//                    deadlineNodeList = deadlineNodeList.stream()
//                            .filter(node -> allowedLinks.contains(node.getName()))
//                            .collect(Collectors.toList());
//                }
//
//                boolean needFilter2 = (
//                        (rectificationInfo.getProjectChildType().contains("主城崛起") &&
//                                rectificationInfo.getProjectChildCategoryType().contains("拟开工重点项目"))
//                                ||
//                                ("项目建设".equals(rectificationInfo.getProjectChildType()) && "拟开工重点项目".equals(rectificationInfo.getProjectChildCategoryType()))
//                );
//                if (needFilter2 && deadlineNodeList != null) {
//                    List<String> allowedLinks = Arrays.asList(
//                            "纳入上级规划/纳入年度投资计划/完成项目建议书批复，启动项目实施",
//                            "完成可行性研究报告批复",
//                            "完成环评批复",
//                            "完成安评手续",
//                            "完成初步设计批复",
//                            "完成财政预算评审",
//                            "完成用地手续批复（用地、工程规划许可证）",
//                            "完成用林手续批复",
//                            "完成项目招投标（含公示）",
//                            "办理施工许可证并开工建设"
//                    );
//                    deadlineNodeList = deadlineNodeList.stream()
//                            .filter(node -> allowedLinks.contains(node.getName()))
//                            .collect(Collectors.toList());
//                }
//            }
//        }
        if (deadlineNodeList != null) {
            // 排除 progress 为 / 的节点
            deadlineNodeList = deadlineNodeList.stream()
                    // 只保留 progress 不为 null 且不等于 "/" 的元素
                    .filter(node -> node.getProgress() == null || !"/".equals(node.getProgress()))
                    .collect(Collectors.toList());
        }
        return deadlineNodeList;
    }

    /**
     * 计算rectification的warnLevel;
     *
     * @return
     */
    private RectificationInfo calcWarnLevel(RectificationInfo rectificationInfo, List<DeadlineNode> deadlineNodeList, List<DeadlineRectificationPeriod> deadlineRectificationPeriods) {
        // === 你现有 currentNode 计算（不变）===
        if (rectificationInfo.getIsNotCalc()) {
            return rectificationInfo;
        }
        Map<Integer, List<DeadlineRectificationPeriod>> deadlineRectificationPeriodMap = new HashMap<>();
        if (deadlineRectificationPeriods != null) {
            deadlineRectificationPeriodMap = deadlineRectificationPeriods.stream().filter(o -> o.getDeadlineNodeId() != null).collect(Collectors.groupingBy(DeadlineRectificationPeriod::getDeadlineNodeId));
        }
        List<DeadlineNode> qualifiedNodes = new ArrayList<>();
        List<DeadlineNode> qualifiedNodesT = new ArrayList<>();
        Date today = new Date();
        String currentNode = "";
        // 第一步：筛选符合条件的节点
        StringBuilder sb = new StringBuilder();
        for (DeadlineNode d : deadlineNodeList) {
            if (deadlineRectificationPeriodMap != null) {
                List<DeadlineRectificationPeriod> deadlineRectificationPeriodList = deadlineRectificationPeriodMap.get(d.getId());

                if (deadlineRectificationPeriodList != null) {
                    // 排序并过滤后的列表
                    List<DeadlineRectificationPeriod> sortedList = deadlineRectificationPeriodList.stream()
                            .filter(item -> item.getRectificationTime() != null)
                            .sorted(Comparator.comparing(DeadlineRectificationPeriod::getRectificationTime,
                                    Comparator.reverseOrder()))
                            .collect(Collectors.toList());

                    // 先判断列表是否有元素，再获取第一个
                    if (!sortedList.isEmpty()) {
                        d.setDeadlineRectificationPeriod(sortedList.get(0).getRectificationTime());
                    }
                }
            }
            // 1. 必须有截止日期
            if (d.getDeadlineRectificationPeriod() == null) {
                d.setDeadlineRectificationPeriod(d.getDeadline());
            }
            if (d.getDeadlineRectificationPeriod() == null) {
                continue;
            } else {
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(d.getDeadlineRectificationPeriod());
                calendar.set(Calendar.HOUR_OF_DAY, 23);
                calendar.set(Calendar.MINUTE, 59);
                calendar.set(Calendar.SECOND, 59);
                d.setDeadlineRectificationPeriod(calendar.getTime());
            }
            Date endTime = d.getEndTime();
            if (endTime == null && d.getDeadlineRectificationPeriod().before(today)) {
                qualifiedNodesT.add(d);
            } else {
//                if (d.getDeadlineRectificationPeriod().after(today)&&endTime != null) {
//                    qualifiedNodes.add(d);
//                }
            }

        }
        if (!qualifiedNodesT.isEmpty()) {
            for (DeadlineNode node : qualifiedNodesT) {
                if (null != node && StringUtils.isNotBlank(node.getName()) && node.getEndTime() == null) {
                    if (node.getName().contains("节点") &&
                            node.getName().length() == 3 &&
                            node.getMainId() > 100000 &&
                            StrUtil.isNotBlank(node.getDepartmentName())) {
                        node.setName(node.getDepartmentName() + "-" + node.getName());
                    }
                    sb.append(node.getName()).append("; "); // 用分号分隔多个节点
                }
            }
        } else if (!qualifiedNodes.isEmpty()) {
            //             第二步：从符合条件的节点中，筛选出最早截止日期的节点
            // 找到最早的截止日期
            Date earliestDeadline = qualifiedNodes.get(0).getDeadlineRectificationPeriod();
            for (DeadlineNode node : qualifiedNodes) {
                if (node.getDeadlineRectificationPeriod().before(earliestDeadline)) {
                    earliestDeadline = node.getDeadlineRectificationPeriod();
                }
            }

            // 拼接所有最早截止日期的节点名称
            for (DeadlineNode node : qualifiedNodes) {
                if (node.getDeadlineRectificationPeriod().equals(earliestDeadline) && node.getEndTime() == null) {
                    if (node.getName().contains("节点") &&
                            node.getName().length() == 3 &&
                            node.getMainId() > 100000 &&
                            StrUtil.isNotBlank(node.getDepartmentName())) {
                        node.setName(node.getDepartmentName() + "-" + node.getName());
                    }
                    sb.append(node.getName()).append("; "); // 用分号分隔多个节点
                }
            }
        }
        // 移除最后一个分隔符
        if (sb.length() > 0) {
            currentNode = sb.substring(0, sb.length() - 2);
        }

        if (StrUtil.isEmptyIfStr(currentNode)) {
            for (DeadlineNode d : deadlineNodeList) {
                if (d.getDeadlineRectificationPeriod() != null) {
                    if (d.getDeadlineRectificationPeriod().after(new Date()) && d.getEndTime() == null) {
                        currentNode = d.getName();
                    }
                }
            }
        }
        rectificationInfo.setCurrentNode(currentNode);
        // === 你现有 warnLevel（按“当前环节”）计算（不变）===
        Integer warnLevel = 0;
        for (DeadlineNode node : deadlineNodeList) {
            boolean isCurrentNode = false;
            if (currentNode.contains(node.getName())) {
                isCurrentNode = true;
                int nodeWarnLevel = Convert.toInt(node.getWarnLevel(), 0);
                if (nodeWarnLevel == 2) {
                    warnLevel = 2;
                    break;
                } else if (warnLevel < 1 && nodeWarnLevel == 1) {
                    warnLevel = 1;
                }
            }
            if (isCurrentNode) {
                int nodeWarnLevel = Convert.toInt(node.getWarnLevel(), 0);
                if (nodeWarnLevel == 2) {
                    warnLevel = 2;
                    break;
                } else if (warnLevel < 1 && nodeWarnLevel == 1) {
                    warnLevel = 1;
                }
            }
        }
        rectificationInfo.setWarnLevel(warnLevel);
        return rectificationInfo;
    }


    @Override
    public Answer<Object> queryRectificationV2Num() {

        LambdaQueryWrapper<RectificationInfo> rectificationInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        rectificationInfoLambdaQueryWrapper.ne(RectificationInfo::getState, "已关闭").ne(RectificationInfo::getState, "已销号").ne(RectificationInfo::getState, "已退出");
        List<RectificationInfo> rectificationInfoList = rectificationInfoMapper.selectList(rectificationInfoLambdaQueryWrapper);
//        List<DeadlineRectificationPeriod> deadlineRectificationPeriods = deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<>());
//        for (RectificationInfo rectificationInfo : rectificationInfoList) {
//            // 原有currentNode和warnLevel计算逻辑（不变）
//            List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(
//                    new LambdaQueryWrapper<DeadlineNode>()
//                            .eq(DeadlineNode::getRectificationId, rectificationInfo.getId())
//                            .orderByDesc(DeadlineNode::getId)
//            );
//            deadlineNodeList = filiterNode(rectificationInfo, deadlineNodeList);
//            RectificationInfo rectificationInfo2 = calcWarnLevel(rectificationInfo, deadlineNodeList,deadlineRectificationPeriods);
//            rectificationInfo.setWarnLevel(rectificationInfo2.getWarnLevel());
//            rectificationInfo.setStatusLabel(rectificationInfo2.getStatusLabel());
//        }

        List<String> departments = Arrays.asList(
                "黄州区", "团风县", "红安县", "麻城市",
                "罗田县", "英山县", "浠水县", "蕲春县",
                "武穴市", "黄梅县", "黄冈高新区","市临空经济区",
                "龙感湖管理区", "白莲河示范区"
        );
        // 3. 分组：包含目标区划名的归到对应区划，否则归为“市本级”
        Map<String, List<RectificationInfo>> deptProjectMap = rectificationInfoList.stream()
                .filter(info -> {
                    // 过滤无有效区划名的项目（避免空key）
                    String deptName = info.getDepartmentName();
                    return deptName != null && !deptName.trim().isEmpty();
                })
                .collect(Collectors.groupingBy(info -> {
                    // 标准化项目的区划名（去空格）
                    String projectDept = info.getDepartmentName().trim();

                    // 核心修改：查找是否包含目标区划名（如“市资建局黄州区”包含“黄州区”）
                    for (String targetDept : departments) {
                        if (projectDept.contains(targetDept)) {
                            return targetDept; // 归到包含的目标区划
                        }
                    }

                    // 若不包含任何目标区划名，归为“市本级”
                    return "市本级";
                }));

        // 确保目标区划列表中所有区划都有对应的key（即使无数据）
        for (String targetDept : departments) {
            deptProjectMap.putIfAbsent(targetDept, new ArrayList<>());
        }

        // 确保“市本级”也有key（即使无数据）
        deptProjectMap.putIfAbsent("市本级", new ArrayList<>());

        List<RectificationV2Vo> rectificationV2VoList = convert(deptProjectMap);
        out.println(rectificationV2VoList);
        // 区划. 红绿灯状态
        List<CompanyData> companyList = companyDataMapper.selectList(new LambdaQueryWrapper<>());
        rectificationV2VoList = matchCompanyLampDataToVo(companyList, rectificationV2VoList);
        out.println(rectificationV2VoList);
        List<EnterpriseData> list2 = enterpriseDataMapper.selectList(new LambdaQueryWrapper<>());
        int cityGreen = 0;
        int cityRed = 0;
        int cityYellow = 0;

        // 1. 统计EnterpriseData中的红绿黄灯数量（逻辑不变）
        for (EnterpriseData data : list2) {
            String lampStatus = data.getLampStatus();
            // 避免lampStatus为null导致空指针
            if (lampStatus == null) {
                cityGreen++;
                continue;
            }
            switch (lampStatus) {
                case "1":
                    cityGreen++;
                    break;
                case "3":
                    cityRed++;
                    break;
                default:
                    cityYellow++;
                    break;
            }
        }

// 2. 累加至“市本级”VO（修正总数计算逻辑）
        for (RectificationV2Vo vo : rectificationV2VoList) {
            if (vo.getStreet() == null) {
                continue;
            }
            String streetName = vo.getStreet();
            if ("市本级".equals(streetName)) {
                // 处理原有值为null的情况（默认0）
                int originalRed = vo.getRed() == null ? 0 : vo.getRed();
                int originalGreen = vo.getGreen() == null ? 0 : vo.getGreen();
                int originalYellow = vo.getYellow() == null ? 0 : vo.getYellow();

                // 累加后的值
                int newRed = originalRed + cityRed;
                int newGreen = originalGreen + cityGreen;
                int newYellow = originalYellow + cityYellow;

                // 赋值累加后的红绿黄灯数
                vo.setRed(newRed);
                vo.setGreen(newGreen);
                vo.setYellow(newYellow);

                // 修正：总数 = 累加后的红 + 绿 + 黄（而非原有总数 + 新增数）
                vo.setTotal(newRed + newGreen + newYellow);
                break; // 假设“市本级”唯一，跳出循环
            }
        }
        out.println(rectificationV2VoList);
        //
        // 第一个固定市本级,后续按照 totalGreenRate 从低到高排序
        rectificationV2VoList = processAndSortRegionList(rectificationV2VoList);
        return Answer.ok(rectificationV2VoList);
    }

    /**
     * 将“区划-项目列表”Map转为RectificationV2Vo列表
     *
     * @param deptProjectMap 分组后的Map（key=区划名，value=该区划的项目列表）
     * @return 封装后的VO列表
     */
    public static List<RectificationV2Vo> convert(Map<String, List<RectificationInfo>> deptProjectMap) {
        List<RectificationV2Vo> voList = new ArrayList<>();
        if (deptProjectMap == null || deptProjectMap.isEmpty()) {
            return voList; // Map为空，返回空列表
        }

        // 遍历Map的每个区划分组
        for (Map.Entry<String, List<RectificationInfo>> entry : deptProjectMap.entrySet()) {
            String deptName = entry.getKey(); // 区划名（如“黄州区”“市本级”）
            List<RectificationInfo> projectList = entry.getValue(); // 该区划的项目列表

            // 1. 统计该区划的核心数据（总数、红绿黄灯数）
            int total = projectList.size(); // 项目总数
            int red = 0; // 红灯数（warnLevel=2）
            int green = 0; // 绿灯数（warnLevel=1）
            int yellow = 0; // 黄灯数（其他warnLevel或null）
            int red828 = 0;
            for (RectificationInfo project : projectList) {
                Integer warnLevel = project.getWarnLevel(); // 获取预警等级
                if (warnLevel == null) {
                    green++; // 无预警等级，默认算黄灯（可按业务调整）
                    continue;
                }
                // 按预警等级统计灯色（可按需修改warnLevel与灯色的对应关系）
                switch (warnLevel) {
                    case 0:
                        green++;
                        break;
                    case 2:
                        red++;
                        break;
                    default:
                        yellow++;
                        break;
                }
                if (StringUtils.isNotBlank(project.getLabel()) && project.getLabel().contains("828")) {
                    switch (warnLevel) {
                        case 2:
                            red828++;
                            break;
                    }
                }
            }

            // 2. 封装为RectificationV2Vo
            RectificationV2Vo vo = new RectificationV2Vo();
            vo.setStreet(deptName); // 区划名赋值给streetName
            vo.setTotal(total);         // 项目总数
            vo.setRed(red);             // 红灯数
            vo.setGreen(green);         // 绿灯数
            vo.setYellow(yellow);       // 黄灯数
            vo.setRed828(red828);
            // （可选）若VO有其他字段（如rate、sort），按需赋值
            // vo.setRate(calculateRate(red, total)); // 示例：计算红灯率
            // vo.setSort(voList.size() + 1);        // 示例：设置排序号

            voList.add(vo);
        }

        return voList;
    }


    public static List<RectificationV2Vo> convert(Map<String, List<RectificationInfo>> deptProjectMap,Map<Integer, List<DeadlineNode>> rectificationIdToNodeMap) {
        List<RectificationV2Vo> voList = new ArrayList<>();
        if (deptProjectMap == null || deptProjectMap.isEmpty()) {
            return voList; // Map为空，返回空列表
        }
        // 遍历Map的每个区划分组
        for (Map.Entry<String, List<RectificationInfo>> entry : deptProjectMap.entrySet()) {
            String deptName = entry.getKey(); // 区划名（如“黄州区”“市本级”）
            List<RectificationInfo> projectList = entry.getValue(); // 该区划的项目列表

            // 1. 统计该区划的核心数据（总数、红绿黄灯数）
            int total = projectList.size(); // 项目总数
            int red = 0; // 红灯数（warnLevel=2）
            int green = 0; // 绿灯数（warnLevel=1）
            int yellow = 0; // 黄灯数（其他warnLevel或null）
            int red828 = 0;
//            int responsibility = 0;
            for (RectificationInfo project : projectList) {
                Integer warnLevel = project.getWarnLevel(); // 获取预警等级
                if (warnLevel == null) {
                    green++; // 无预警等级，默认算黄灯（可按业务调整）
                    continue;
                }
                // 按预警等级统计灯色（可按需修改warnLevel与灯色的对应关系）
                switch (warnLevel) {
                    case 0:
                        green++;
                        break;
                    case 2:
                        red++;
                        break;
                    default:
                        yellow++;
                        break;
                }
                if (StringUtils.isNotBlank(project.getLabel()) && project.getLabel().contains("828")) {
                    switch (warnLevel) {
                        case 2:
                            red828++;
                            break;
                    }
                }
//                List<DeadlineNode> deadlineNodes = rectificationIdToNodeMap.get(project.getId());
//                List<String> departmentNames = new ArrayList<>();
//                if (deadlineNodes!=null&&!deadlineNodes.isEmpty()) {
//                    departmentNames = deadlineNodes.stream()
//                            .filter(node -> node.getDepartmentName() != null && !node.getDepartmentName().trim().isEmpty())
//                            .map(DeadlineNode::getDepartmentName)
//                            .distinct() // 去重（基于String的equals方法）
//                            .collect(Collectors.toList());
//                }
//                if (!departmentNames.contains(project.getDepartmentName())) {
//                    out.println(Arrays.toString(departmentNames.toArray()) + ", ==== +" + project.getDepartmentName());
//                    responsibility++;
//                }

            }

            // 2. 封装为RectificationV2Vo
            RectificationV2Vo vo = new RectificationV2Vo();
            vo.setStreet(deptName); // 区划名赋值给streetName
            vo.setTotal(total);         // 项目总数
            vo.setRed(red);             // 红灯数
            vo.setGreen(green);         // 绿灯数
            vo.setYellow(yellow);       // 黄灯数
            vo.setRed828(red828);
//            vo.setResponsibility(responsibility);
            // （可选）若VO有其他字段（如rate、sort），按需赋值
            // vo.setRate(calculateRate(red, total)); // 示例：计算红灯率
            // vo.setSort(voList.size() + 1);        // 示例：设置排序号

            voList.add(vo);
        }

        return voList;
    }

    /**
     * 修正后的匹配方法：严格保留市本级原始数据，其他区划正确累加company_data，确保灯数一致
     */
    public List<RectificationV2Vo> matchCompanyLampDataToVo(
            List<CompanyData> companyList,
            List<RectificationV2Vo> rectificationV2VoList) {

        // 1. 按departmentName分组统计（强化区划名标准化，确保匹配）
        Map<String, int[]> lampCountMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(companyList)) {
            for (CompanyData company : companyList) {
                String deptName = company.getDepartmentName();
                if (deptName == null || deptName.trim().isEmpty()) {
                    continue;
                }
                // 强化标准化：去除所有空格和特殊字符（避免"浠水县 "和"浠水县"不匹配）
                deptName = deptName.trim().replaceAll("\\s+", "");

                int[] countArr = lampCountMap.getOrDefault(deptName, new int[3]); // [红,绿,黄]

                String lampStatus = company.getLampStatus();
                // 严格对应：1=绿灯，2=红灯，3=黄灯（与company_data数据一致）
                if ("1".equals(lampStatus)) {
                    countArr[1]++; // 绿灯（索引1）
                } else if ("2".equals(lampStatus)) {
                    countArr[0]++; // 红灯（索引0）
                } else if ("3".equals(lampStatus)) { // 明确处理黄灯（原代码用else可能包含无效状态）
                    countArr[2]++; // 黄灯（索引2）
                }
                lampCountMap.put(deptName, countArr);
            }
        }

        // 打印company_data统计结果（辅助排查：确认各区划的红绿灯数是否正确）
        out.println("company_data按区划统计：");
        for (Map.Entry<String, int[]> entry : lampCountMap.entrySet()) {
            out.println(entry.getKey() + "：红=" + entry.getValue()[0] +
                    "，绿=" + entry.getValue()[1] +
                    "，黄=" + entry.getValue()[2]);
        }

        // 2. 匹配赋值：严格排除市本级，其他区划累加（确保原始数据正确叠加）
        if (CollectionUtil.isNotEmpty(rectificationV2VoList)) {
            for (RectificationV2Vo vo : rectificationV2VoList) {
                String streetName = vo.getStreet();
                if (streetName == null) {
                    continue;
                }
                // 强化标准化：与company_data的区划名处理保持一致
                streetName = streetName.trim().replaceAll("\\s+", "");

                // 核心：严格保留市本级原始数据，不做任何处理
                if ("市本级".equals(streetName)) {
                    continue;
                }

                // 非市本级：有匹配数据则累加
                int[] countArr = lampCountMap.get(streetName);
                if (countArr != null) {
                    // 处理原有值为null的情况（默认0）
                    int originalRed = (vo.getRed() == null) ? 0 : vo.getRed();
                    int originalGreen = (vo.getGreen() == null) ? 0 : vo.getGreen();
                    int originalYellow = (vo.getYellow() == null) ? 0 : vo.getYellow();

                    // 累加计算（原始值 + company_data对应值）
                    int newRed = originalRed + countArr[0];
                    int newGreen = originalGreen + countArr[1];
                    int newYellow = originalYellow + countArr[2];
                    int newTotal = newRed + newGreen + newYellow; // 重新计算总数（确保与各灯数一致）

                    // 打印累加前后对比（辅助排查）
                    out.println("区划：" + streetName +
                            "，原始红=" + originalRed + "，新红=" + newRed +
                            "；原始绿=" + originalGreen + "，新绿=" + newGreen +
                            "；原始黄=" + originalYellow + "，新黄=" + newYellow);

                    // 更新VO
                    vo.setRed(newRed);
                    vo.setGreen(newGreen);
                    vo.setYellow(newYellow);
                    vo.setTotal(newTotal); // 总数必须是红+绿+黄的和
                }
                // 无匹配数据：保留原始值（不做操作）
            }
        }

        return rectificationV2VoList;
    }

    // 大屏的统计分析
    @Override
    public Answer<Object> queryStaticAllScreen() {
        //
        JSONObject resultJson = new JSONObject();

        List<RectificationInfo> rectificationInfoList = fetchAllRectification(null);
//        List<DeadlineRectificationPeriod> deadlineRectificationPeriods = deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<>());

// 1. 收集所有需要的rectificationId
        List<Integer> rectificationIds = rectificationInfoList.stream()
                .map(RectificationInfo::getId) // 假设id是Long类型，根据实际类型调整
                .collect(Collectors.toList());

// 2. 批量查询所有相关的DeadlineNode（1次查询）
        List<DeadlineNode> allDeadlineNodes = deadlineNodeMapper.selectList(
                new LambdaQueryWrapper<DeadlineNode>()
                        .in(DeadlineNode::getRectificationId, rectificationIds) // 批量匹配
                        .orderByDesc(DeadlineNode::getId) // 保持原有排序逻辑
        );
// 3. 按rectificationId分组，缓存到Map（key: rectificationId，value: 对应的DeadlineNode列表）
        Map<Integer, List<DeadlineNode>> nodeMap = allDeadlineNodes.stream()
                .collect(Collectors.groupingBy(DeadlineNode::getRectificationId));

        out.println("queryStaticAllScreen1 " + new Date().getTime());

        out.println("queryStaticAllScreen2 " + new Date().getTime());
        List<RectificationV2Vo> cityList = getCityData(rectificationInfoList);
        out.println("queryStaticAllScreen3 " + new Date().getTime());
        resultJson.put("cityList", cityList);
        List<RectificationInfo> warnList = rectificationInfoList.stream()
                .filter(Objects::nonNull)  // 过滤掉null元素
                .filter(item -> (item.getWarnLevel()!=null && item.getWarnLevel() == 1))  // 过滤warnLevel为1的元素
                .collect(Collectors.toList());
//        List<RectificationInfo> warnList = rectificationInfoList.stream().filter(item -> item.getWarnLevel() == 1).collect(Collectors.toList());
        out.println("warnList.size() == " + warnList.size());
        List<String> projectTypeList = Arrays.asList(
                "高质量发展", "民生保障", "防风险促稳定", "学查改常态化作风建设","其他重点事项"
        );
        out.println("queryStaticAllScreen4 " + new Date().getTime());
        // 3. 分组：目标区划内的按原区划名分组，不在内的归为“市本级”
        Map<String, List<RectificationInfo>> deptProjectMap = rectificationInfoList.stream()
                .filter(info -> {
                    // 先过滤无有效区划名的项目（避免空key）
                    String projectType = info.getProjectType();
                    return projectType != null && !projectType.trim().isEmpty();
                })
                .collect(Collectors.groupingBy(info -> {
                    // 标准化项目的区划名（去空格，统一匹配标准）
                    String projectType = info.getProjectType().trim();
                    // 判断：若在目标区划列表中，用原区划名；否则归为“市本级”
                    return projectType;
                }));
        // （可选）若目标区划列表中某个区划无项目，主动添加空列表（避免key缺失）
        for (String targetDept : projectTypeList) {
            deptProjectMap.putIfAbsent(targetDept, new ArrayList<>());
        }
        List<RectificationV2Vo> rectificationV2VoList2 = convert(deptProjectMap);
        // java 固定排序
        List<String> customOrder = Arrays.asList("高质量发展", "民生保障", "防风险促稳定",
                "学查改常态化作风建设","其他重点事项");
        Map<String, Integer> orderMap = new HashMap<>();
        for (int i = 0; i < customOrder.size(); i++) {
            orderMap.put(customOrder.get(i), i);
        }
        out.println("queryStaticAllScreen5 " + new Date().getTime());
        // 按照自定义顺序排序
        rectificationV2VoList2.sort((item1, item2) -> {
            // 获取两个元素的顺序索引
            int index1 = orderMap.getOrDefault(item1.getStreet(), customOrder.size());
            int index2 = orderMap.getOrDefault(item2.getStreet(), customOrder.size());
            return Integer.compare(index1, index2);
        });
        resultJson.put("typeStatic", rectificationV2VoList2);
        int total = 0;
        int totalGreen = 0;
        int totalRed = 0;
        int totalYellow = 0;
        for (int i = 0; i < rectificationV2VoList2.size(); i++) {
            total = total + rectificationV2VoList2.get(i).getTotal();
            totalGreen = totalGreen + rectificationV2VoList2.get(i).getGreen();
            totalRed = totalRed + rectificationV2VoList2.get(i).getRed();
            totalYellow = totalYellow + rectificationV2VoList2.get(i).getYellow();
        }
        RectificationV2Vo rectificationStaticVo = new RectificationV2Vo();
        rectificationStaticVo.setTotal(total);
        rectificationStaticVo.setGreen(totalGreen);
        rectificationStaticVo.setRed(totalRed);
        rectificationStaticVo.setYellow(totalYellow);
        out.println("queryStaticAllScreen6 " + new Date().getTime());
        resultJson.put("totalStatic", rectificationStaticVo);
        List<RectificationV2Vo> streetList = getStreetData(rectificationInfoList);
        resultJson.put("streetList", streetList);
        out.println("queryStaticAllScreen7 " + new Date().getTime());
        return Answer.ok(resultJson);
    }

    private List<RectificationV2Vo> getCityData(List<RectificationInfo> rectificationInfoList) {
        // 确保输入列表的顺序一致
//        if (rectificationInfoList != null) {
//            rectificationInfoList.sort(Comparator.comparing(RectificationInfo::getId));
//        }
        // 处理部门拆分
        List<RectificationInfo> processedList = RectificationClassifier.process(rectificationInfoList);

        // 按部门合并
        List<RectificationV2Vo> rectificationV2VoList = RectificationClassifier.mergeByDepartment(processedList);

        if (CollectionUtil.isEmpty(rectificationV2VoList)) {
            return new ArrayList<>();
        }

        // 只按红灯率(rate)升序排序
        // 先按红灯率升序（低的在前），红灯率相同则按绿灯数量降序（多的在前）
        rectificationV2VoList.sort((vo1, vo2) -> {
            // 1. 处理"50.00%"这种格式的rate字符串，按红灯率升序排序
            BigDecimal rate1 = parseRate(vo1.getRate());
            BigDecimal rate2 = parseRate(vo2.getRate());
            int rateCompare = rate1.compareTo(rate2);
            // 如果红灯率不同，直接按红灯率排序结果返回
            if (rateCompare != 0) {
                return rateCompare;
            }
            // 2. 红灯率相同时，按绿灯数量降序排序（绿灯多的在前）
            // 假设获取绿灯数量的方法为getGreenLightCount()，请根据实际方法名修改
            int greenLightCount1 = vo1.getGreen();
            int greenLightCount2 = vo2.getGreen();
            // 降序排序：后减前（与升序相反）
            return Integer.compare(greenLightCount2, greenLightCount1);
        });
        // 设置排序序号
        for (int i = 0; i < rectificationV2VoList.size(); i++) {
            rectificationV2VoList.get(i).setSort(i + 1);
        }
        return rectificationV2VoList;
    }

    // 辅助方法：解析百分比字符串
    private BigDecimal parseRate(String rateStr) {
        if (rateStr == null || rateStr.trim().isEmpty()) {
            return BigDecimal.ZERO;
        }
        // 去除百分号并转换为数字
        String numericStr = rateStr.replace("%", "").trim();
        try {
            return new BigDecimal(numericStr);
        } catch (NumberFormatException e) {
            // 解析失败时返回0
            return BigDecimal.ZERO;
        }
    }

//    private List<RectificationV2Vo> getStreetData(List<RectificationInfo> rectificationInfoList) {
//        List<String> departments = Arrays.asList(
//                "黄州区", "团风县", "红安县", "麻城市",
//                "罗田县", "英山县", "浠水县", "蕲春县",
//                "武穴市", "黄梅县", "黄冈高新区","市临空经济区",
//                "龙感湖管理区", "白莲河示范区"
//        );
//        // 3. 分组：包含目标区划名的归到对应区划，否则归为“市本级”
//        Map<String, List<RectificationInfo>> deptProjectMap = rectificationInfoList.stream()
//                .filter(info -> {
//                    // 过滤无有效区划名的项目（避免空key）
//                    String deptName = info.getDepartmentName();
//                    return deptName != null && !deptName.trim().isEmpty();
//                })
//                .collect(Collectors.groupingBy(info -> {
//                    // 标准化项目的区划名（去空格）
//                    String projectDept = info.getDepartmentName().trim();
//
//                    // 核心修改：查找是否包含目标区划名（如“市资建局黄州区”包含“黄州区”）
//                    for (String targetDept : departments) {
//                        if (projectDept.contains(targetDept)) {
//                            return targetDept; // 归到包含的目标区划
//                        }
//                    }
//                    // 若不包含任何目标区划名，归为“市本级”
//                    return "市本级";
//                }));
//        // 查询所有ticket表;也不对,
//        List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(new LambdaQueryWrapper<>());
//        List<DeadlineNode> filteredDeadlineNodes = deadlineNodeList.stream()
//                // 第一步：过滤无有效部门名称的节点（去空）
//                .filter(node -> {
//                    String deptName = node.getDepartmentName();
//                    return deptName != null && !deptName.trim().isEmpty();
//                })
//                // 第二步：保留匹配目标区划或归为市本级的节点（核心过滤）
//                .filter(node -> {
//                    String nodeDept = node.getDepartmentName().trim();
//                    // 检查是否包含任意目标区划名
//                    boolean matchesTargetDept = departments.stream()
//                            .anyMatch(targetDept -> nodeDept.contains(targetDept));
//                    // 保留「匹配目标区划」或「部门名是市本级」的节点（可按需修改逻辑）
//                    return matchesTargetDept || "市本级".equals(nodeDept);
//                })
//                .collect(Collectors.toList());
//        Map<Integer, List<DeadlineNode>> rectificationIdToNodeMap = filteredDeadlineNodes.stream()
//                .filter(node -> node.getRectificationId() != null)
//                .collect(Collectors.groupingBy(DeadlineNode::getRectificationId));
//        // 确保目标区划列表中所有区划都有对应的key（即使无数据）
//        for (String targetDept : departments) {
//            deptProjectMap.putIfAbsent(targetDept, new ArrayList<>());
//        }
//
//        // 确保“市本级”也有key（即使无数据）
//        deptProjectMap.putIfAbsent("市本级", new ArrayList<>());
//        List<RectificationV2Vo> rectificationV2VoList = convert(deptProjectMap,rectificationIdToNodeMap);
//        // 相同
//        return processAndSortRegionList(rectificationV2VoList);
//    }

    private List<RectificationV2Vo> getStreetData(List<RectificationInfo> rectificationInfoList) {
        List<String> departments = Arrays.asList(
                "黄州区", "团风县", "红安县", "麻城市",
                "罗田县", "英山县", "浠水县", "蕲春县",
                "武穴市", "黄梅县", "黄冈高新区","市临空经济区",
                "龙感湖管理区", "白莲河示范区"
        );
        // 3. 分组：包含目标区划名的归到对应区划，否则归为“市本级”
        Map<String, List<RectificationInfo>> deptProjectMap = rectificationInfoList.stream()
                .filter(info -> {
                    // 过滤无有效区划名的项目（避免空key）
                    String deptName = info.getDepartmentName();
                    return deptName != null && !deptName.trim().isEmpty();
                })
                .collect(Collectors.groupingBy(info -> {
                    // 标准化项目的区划名（去空格）
                    String projectDept = info.getDepartmentName().trim();

                    // 核心修改：查找是否包含目标区划名（如“市资建局黄州区”包含“黄州区”）
                    for (String targetDept : departments) {
                        if (projectDept.contains(targetDept)) {
                            return targetDept; // 归到包含的目标区划
                        }
                    }
                    // 若不包含任何目标区划名，归为“市本级”
                    return "市本级";
                }));

        // 查询所有 deadlineNode 并构建 整改ID -> 节点列表 的映射（无需提前过滤，后续按需筛选）
        List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(new LambdaQueryWrapper<>());
        Map<Integer, List<DeadlineNode>> rectificationIdToNodeMap = deadlineNodeList.stream()
                .filter(node -> node.getRectificationId() != null)
                .filter(node -> node.getDepartmentName() != null && !node.getDepartmentName().trim().isEmpty())
                .collect(Collectors.groupingBy(DeadlineNode::getRectificationId));

        // 确保目标区划列表中所有区划都有对应的key（即使无数据）
        for (String targetDept : departments) {
            deptProjectMap.putIfAbsent(targetDept, new ArrayList<>());
        }
        // 确保“市本级”也有key（即使无数据）
        deptProjectMap.putIfAbsent("市本级", new ArrayList<>());

        // 转换并统计（传入所有整改数据和节点映射，用于计算responsibility）
        List<RectificationV2Vo> rectificationV2VoList = convert(deptProjectMap, rectificationIdToNodeMap, rectificationInfoList, departments);
        // 排序返回
        return processAndSortRegionList(rectificationV2VoList);
    }


    /**
     * 转换并统计数据（新增 responsibility 统计逻辑）
     * @param deptProjectMap 区划分组的整改任务Map
     * @param rectificationIdToNodeMap 整改ID关联的节点Map
     * @param allRectificationList 所有整改任务列表（用于统计跨区划责任）
     * @param targetDepartments 目标区划列表
     * @return 封装后的VO列表
     */
    public static List<RectificationV2Vo> convert(
            Map<String, List<RectificationInfo>> deptProjectMap,
            Map<Integer, List<DeadlineNode>> rectificationIdToNodeMap,
            List<RectificationInfo> allRectificationList,
            List<String> targetDepartments) {

        List<RectificationV2Vo> voList = new ArrayList<>();
        if (deptProjectMap == null || deptProjectMap.isEmpty()) {
            return voList; // Map为空，返回空列表
        }

        // 遍历每个区划分组
        for (Map.Entry<String, List<RectificationInfo>> entry : deptProjectMap.entrySet()) {
            String currentRegion = entry.getKey(); // 当前统计的县市区（如“黄州区”“市本级”）
            List<RectificationInfo> projectList = entry.getValue(); // 该区划的直接归属项目

            // 1. 统计核心数据（总数、红绿黄灯数、828红灯数）
            int total = projectList.size();
            int red = 0, green = 0, yellow = 0, red828 = 0;
            for (RectificationInfo project : projectList) {
                Integer warnLevel = project.getWarnLevel();
                if (warnLevel == null) {
                    green++;
                    continue;
                }
                switch (warnLevel) {
                    case 0: green++; break;
                    case 2: red++; break;
                    default: yellow++; break;
                }
                // 统计828标签的红灯数
                if (StringUtils.isNotBlank(project.getLabel()) && project.getLabel().contains("828") && warnLevel == 2) {
                    red828++;
                }
            }

            // 2. 核心新增：统计 responsibility（跨区划责任数）
            int responsibility = countResponsibility(currentRegion, allRectificationList, rectificationIdToNodeMap, targetDepartments);

            // 3. 封装VO
            RectificationV2Vo vo = new RectificationV2Vo();
            vo.setStreet(currentRegion);
            vo.setTotal(total);
            vo.setRed(red);
            vo.setGreen(green);
            vo.setYellow(yellow);
            vo.setRed828(red828);
            vo.setResponsibility(responsibility); // 新增字段赋值

            voList.add(vo);
        }

        return voList;
    }

    /**
     * 统计当前区划的 responsibility 数量
     * 规则：整改任务的departmentName不含当前区划，但关联的DeadlineNode的departmentName包含当前区划
     * @param currentRegion 当前统计的区划（如“黄州区”）
     * @param allRectificationList 所有整改任务列表
     * @param rectificationIdToNodeMap 整改ID -> 节点列表映射
     * @param targetDepartments 目标区划列表（用于排除市本级特殊逻辑）
     * @return 符合条件的任务数
     */
    private static int countResponsibility(
            String currentRegion,
            List<RectificationInfo> allRectificationList,
            Map<Integer, List<DeadlineNode>> rectificationIdToNodeMap,
            List<String> targetDepartments) {

        // 市本级特殊处理：统计所有整改任务中，主表不含任何区县、但节点含区县的数量（可按需调整）
        if ("市本级".equals(currentRegion)) {
            return 0; // 若市本级无需统计responsibility，直接返回0；需统计则调整逻辑
        }

        // 遍历所有整改任务，筛选符合条件的
        return (int) allRectificationList.stream()
                .filter(rectification -> {
                    // 条件1：整改任务的departmentName 不包含 当前区划名
                    String deptName = rectification.getDepartmentName();
                    if (deptName == null || deptName.trim().isEmpty()) {
                        return false; // 无有效部门名，排除
                    }
                    boolean mainDeptContainsRegion = deptName.trim().contains(currentRegion);
                    if (mainDeptContainsRegion) {
                        return false; // 主表包含当前区划，排除
                    }
                    // 条件2：该整改任务关联的DeadlineNode中，存在departmentName包含当前区划名的节点
                    List<DeadlineNode> nodes = rectificationIdToNodeMap.get(rectification.getId());
                    if (nodes == null || nodes.isEmpty()) {
                        return false; // 无关联节点，排除
                    }
                    return nodes.stream()
                            .anyMatch(node -> node.getDepartmentName().trim().contains(currentRegion));
                })
                .count();
    }

    /**
     * 根据部门查询节点红灯数
     *
     * @return
     */
    @Override
    public Answer<Object> queryRectificationNodeCity() {

        List<RectificationInfo> rectificationInfoList = fetchAllRectification("");
        if (CollectionUtils.isEmpty(rectificationInfoList)) {
            return Answer.ok(new ArrayList<>()); // 空集合直接返回，避免无效处理
        }

// 1. 收集所有需要查询的 rectificationId（Integer类型）
        List<Integer> rectificationIds = rectificationInfoList.stream()
                .map(RectificationInfo::getId) // 假设ID字段是Integer类型
                .collect(Collectors.toList());

// 2. 批量查询所有关联的DeadlineNode（1次SQL替代N次）
        List<DeadlineNode> allDeadlineNodes = deadlineNodeMapper.selectList(
                new LambdaQueryWrapper<DeadlineNode>()
                        .in(DeadlineNode::getRectificationId, rectificationIds) // 用in批量查询
                        .orderByDesc(DeadlineNode::getId) // 保持原有排序
        );

        // 3. 按rectificationId（Integer）分组，方便内存匹配
        Map<Integer, List<DeadlineNode>> nodeGroupByRectId = allDeadlineNodes.stream()
                .collect(Collectors.groupingBy(DeadlineNode::getRectificationId)); // 键为Integer类型

        List<DeadlineNode> deadlineNodes = new ArrayList<>();

        // 4. 遍历rectificationInfoList，处理节点（内存操作，无SQL）
        for (RectificationInfo rectInfo : rectificationInfoList) {
            Integer rectId = rectInfo.getId(); // 取出当前rectification的ID（Integer）
            // 从分组中获取当前ID对应的节点列表（无则返回空集合）
            List<DeadlineNode> deadlineNodeList = nodeGroupByRectId.getOrDefault(rectId, new ArrayList<>());

            // 原有过滤逻辑
            deadlineNodeList = filiterNode(rectInfo, deadlineNodeList);

            // 处理每个节点的部门名称
            for (DeadlineNode node : deadlineNodeList) {
                String deptName = node.getDepartmentName();

                // 判断是否为市级部门（复用原有逻辑）
                boolean isCityLevelDepartment = !COUNTY_LEVEL_DISTRICTS2.contains(rectInfo.getDepartmentName());
                if (!isCityLevelDepartment) {
                    continue;
                }
                if (deptName == null) {
                    continue; // 跳过部门名为空的节点
                }

                // 部门名称标准化（保持原有替换逻辑）
                deptName = deptName.replace("发改", "市发改委")
                        .replace("资建", "市资建局")
                        .replace("林业", "市林业局")
                        .replace("财政", "市财政局")
                        .replace("生态", "市生态环境局")
                        .replace("应急", "市应急管理局")
                        .replace("自规部门", "市自然资源规划局")
                        .replace("住建", "市住新局");
                node.setDepartmentName(deptName);

                if (StrUtil.isNotBlank(deptName)) {
                    deadlineNodes.add(node);
                }
            }
        }
        // 后续处理保持不变
        deadlineNodes = SmartDepartmentSplitter.splitNodes(deadlineNodes);
        List<Map<String, Object>> statistics = DeadlineNodeStatistics.groupByDepartment(deadlineNodes);
        deadlineNodes = DeadlineNodeStatistics.convertToDeadlineNodes(statistics);
        deadlineNodes = DeadlineNodeStatistics.sortByRateAsc(deadlineNodes);
        return Answer.ok(deadlineNodes);
    }


    public List<DeadlineNode> process(Map<String, List<RectificationInfo>> rectificationInfoMap) {
        // 1. 新增：全量数据容器，收集所有循环中的有效DeadlineNode
        List<DeadlineNode> allDeadlineNodes = new ArrayList<>();

        // 循环处理每个key对应的RectificationInfo列表
        for (String key : rectificationInfoMap.keySet()) {
            List<RectificationInfo> rectificationInfos = rectificationInfoMap.get(key);
            // 提取需要查询的id列表
            List<Integer> ids = rectificationInfos.stream()
                    .map(RectificationInfo::getId)
                    .collect(Collectors.toList());

            // 2. 查询当前key对应的截止节点数据
            List<DeadlineNode> currentNodeList = deadlineNodeMapper.selectList(
                    new LambdaQueryWrapper<DeadlineNode>()
                            .in(DeadlineNode::getRectificationId, ids) // 按id筛选
                            .orderByDesc(DeadlineNode::getId)
            );

            // 3. 过滤当前列表中无效数据（截止日期为空的节点）
            List<DeadlineNode> validCurrentNodes = currentNodeList.stream()
                    .filter(node -> node.getDeadline() != null) // 仅保留有截止日期的节点
                    .collect(Collectors.toList());

            // 4. 将当前循环的有效节点，添加到全量容器中（暂不分组，只收集）
            allDeadlineNodes.addAll(validCurrentNodes);
        }

        // -------------------------- 所有循环结束后，统一处理全量数据 --------------------------
        // 5. 全量数据：部门名称标准化 + 赋值street，并过滤掉黄冈高新区和黄州区的记录
        List<DeadlineNode> filteredNodes = new ArrayList<>();
        for (DeadlineNode node : allDeadlineNodes) {
            String deptName = node.getDepartmentName();
            if (deptName == null) continue; // 跳过部门名为空的节点
            // 过滤掉包含黄冈高新区和黄州区的记录
            if (deptName.contains("黄冈高新区") || deptName.contains("黄州区")) {
                continue;
            }
            // 部门名称标准化（统一格式）
            if (deptName.contains("发改")) {
                node.setDepartmentName("市发改委");
            } else if (deptName.contains("资建部门")) {
                node.setDepartmentName("市资建局");
            } else if (deptName.contains("林业部门")) {
                node.setDepartmentName("市林业局");
            } else if (deptName.contains("生态环境部门")) {
                node.setDepartmentName("市生态环境局");
            } else if (deptName.contains("项目单位")) {
                node.setDepartmentName(deptName.replace("项目单位", ""));
            }
            node.setDepartmentName(deptName.replace(" ", ""));
            // 其他部门不修改，保留原始名称

            // 用标准化后的部门名作为street（最终分组的依据）
            node.setStreet(node.getDepartmentName());
            filteredNodes.add(node);
        }

        // 6. 全量数据：按部门分组汇总（核心：同一部门的所有数据合并统计）
        Map<String, DeadlineNode> deptSummaryMap = new HashMap<>();
        for (DeadlineNode node : filteredNodes) {
            String street = node.getStreet();
            if (street == null || street.trim().isEmpty()) continue; // 跳过street为空的节点

            // 初始化：若部门首次出现，创建汇总对象并设置基础信息
            deptSummaryMap.putIfAbsent(street, new DeadlineNode() {{
                setStreet(street);       // 部门名称（标准化后）
                setRed(0);               // 红灯数初始为0
                setGreen(0);             // 绿灯数初始为0
                setYellow(0);            // 黄灯数初始为0
                setTotal(0);             // 总数初始为0
            }});

            // 累加统计：根据当前节点的warnLevel，更新部门汇总数据
            DeadlineNode deptSummary = deptSummaryMap.get(street);
            switch (node.getWarnLevel()) {
                case "2": // 假设warnLevel=2代表红灯
                    deptSummary.setRed(deptSummary.getRed() + 1);
                    break;
                case "1": // 假设warnLevel=1代表黄灯
                    deptSummary.setYellow(deptSummary.getYellow() + 1);
                    break;
                default:  // 其他情况代表绿灯
                    deptSummary.setGreen(deptSummary.getGreen() + 1);
                    break;
            }
            deptSummary.setTotal(deptSummary.getTotal() + 1); // 总数+1
        }

        // 7. 转换为最终列表（每个部门一条汇总数据）
        List<DeadlineNode> finalResult = new ArrayList<>(deptSummaryMap.values());

        // 8. 计算每个部门的红灯率（红灯数/总数）
        for (DeadlineNode deptSummary : finalResult) {
            String redRate;
            Integer total = deptSummary.getTotal();
            Integer red = deptSummary.getRed();

            // 避免除数为0，总数为0时红灯率默认为0.00%
            if (total == null || total == 0) {
                redRate = "0.0%";
            } else {
                // 用BigDecimal确保精度，保留2位小数
                BigDecimal rate = new BigDecimal(red == null ? 0 : red)
                        .divide(new BigDecimal(total), 4, RoundingMode.HALF_UP) // 先保留4位，避免四舍五入误差
                        .multiply(new BigDecimal(100)) // 转为百分比
                        .setScale(1, RoundingMode.HALF_UP); // 最终保留2位小数
                redRate = rate + "%";
            }
            deptSummary.setRate(redRate);
        }

        // 过滤特定单位
        finalResult = finalResult.stream()
                .filter(node -> !node.getStreet().contains("项目单位"))
                .filter(node -> !node.getStreet().contains("审批立项单位"))
                .collect(Collectors.toList());

        // 9. 按红灯率降序排序（红灯率高的排在前面）
        finalResult.sort((node1, node2) -> {
            // 提取百分比数值（去掉%符号），转为BigDecimal比较
            BigDecimal rate1 = new BigDecimal(node1.getRate().replace("%", ""));
            BigDecimal rate2 = new BigDecimal(node2.getRate().replace("%", ""));
            return rate2.compareTo(rate1); // 降序：rate2 - rate1，大的在前
        });

        // 10. 设置排序序号（1,2,3...）
        for (int i = 0; i < finalResult.size(); i++) {
            finalResult.get(i).setRateSort(i + 1);
        }

        return finalResult;
    }

    @Override
    public Answer<Object> queryRectificationV2NumCity() {
        List<RectificationV2Vo> rectificationV2VoList = new ArrayList<>();
        List<RectificationInfo> rectificationInfoList = fetchAllRectification(null);
//        List<DeadlineRectificationPeriod> deadlineRectificationPeriods = deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<>());

//        for (RectificationInfo rectificationInfoItem : rectificationInfoList) {
//            // ---------------------- 原有currentNode和warnLevel计算逻辑（不变）----------------------
//            List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(
//                    new LambdaQueryWrapper<DeadlineNode>()
//                            .eq(DeadlineNode::getRectificationId, rectificationInfoItem.getId())
//                            .orderByDesc(DeadlineNode::getId)
//            );
//            deadlineNodeList = filiterNode(rectificationInfoItem,deadlineNodeList);
//            RectificationInfo  rectificationInfoItem2 = calcWarnLevel(rectificationInfoItem,deadlineNodeList,deadlineRectificationPeriods);
//            rectificationInfoItem.setWarnLevel(rectificationInfoItem2.getWarnLevel());
//        }
        rectificationV2VoList = getCityData(rectificationInfoList);
        return Answer.ok(rectificationV2VoList);
    }


    // 处理原始数据：拆分部门、过滤黄州区、计算绿灯率
    public List<RectificationV2Vo> processRawData(List<RectificationV2Vo> rawResultList) {
        List<RectificationV2Vo> finalList = new ArrayList<>();
        for (RectificationV2Vo raw : rawResultList) {
            // 1. 拆分部门（处理可能的换行符，再按中文顿号分割）
            String street = raw.getStreet().replace("\n", "").trim(); // 清除换行符和空格
            String[] departments = street.split("、");
            // 2. 遍历拆分后的部门，过滤黄州区
            for (String dept : departments) {
                if (dept.contains("黄州区")) {
                    continue; // 过滤黄州区数据
                }
                // 3. 构建RectificationV2Vo对象
                RectificationV2Vo vo = new RectificationV2Vo();
                vo.setStreet(dept);
                vo.setRed(raw.getRed());
                vo.setGreen(raw.getGreen());
                vo.setYellow(raw.getYellow());
                vo.setTotal(raw.getTotal());
                vo.setRate(raw.getRate());
                vo.setSort(raw.getSort());
                // greenFinish/greenNotFinish无原始数据，暂设为null
                vo.setGreenFinish(null);
                vo.setGreenNotFinish(null);
                // 4. 计算totalGreenRate（绿灯率）
                vo.setTotalGreenRate(calculateGreenRate(raw.getGreen(), raw.getTotal()));
                finalList.add(vo);
            }
        }
        return finalList;
    }

    // 计算绿灯率：保留2位小数，格式为"XX.XX%"
    private String calculateGreenRate(Integer green, Integer total) {
        if (total == null || total == 0) {
            return "0.0%";
        }
        if (green == null) {
            green = 0;
        }
        // 用BigDecimal避免浮点数精度问题
        BigDecimal greenBig = new BigDecimal(green);
        BigDecimal totalBig = new BigDecimal(total);
        BigDecimal rate = greenBig.divide(totalBig, 4, RoundingMode.HALF_UP) // 先保留4位小数
                .multiply(new BigDecimal(100)) // 乘以100转为百分比
                .setScale(1, RoundingMode.HALF_UP); // 最终保留2位小数
        return rate + "%";
    }

    @Override
    public Answer<Object> queryRectificationV2ProjectType(RectificationInfo rectificationInfo) {

        List<RectificationV2Vo> rectificationV2VoList = new ArrayList<>();

        if (rectificationInfo.getProjectType() != null) {
            List<RectificationInfo> rectificationInfoList = fetchAllRectification("");
            rectificationInfoList = rectificationInfoList.stream().filter(x -> x.getProjectType().equals(rectificationInfo.getProjectType())).collect(Collectors.toList());
            ;
            Map<String, List<RectificationInfo>> rectificationInfoMap = new HashMap<>();
            if (null != rectificationInfoList) {
                rectificationInfoMap = rectificationInfoList.stream().filter(x -> x.getProjectChildType() != null).collect(Collectors.groupingBy(RectificationInfo::getProjectChildType));
            }
            List<DeadlineRectificationPeriod> deadlineRectificationPeriods = deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<>());
            for (String key : rectificationInfoMap.keySet()) {
                List<RectificationInfo> rectificationInfos = rectificationInfoMap.get(key);
                int red = 0;
                int green = 0;
                int yellow = 0;
                RectificationV2Vo rectificationV2Vo = new RectificationV2Vo();
                rectificationV2Vo.setStreet(key);
                for (RectificationInfo rectificationInfo1 : rectificationInfos) {
//                    List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(
//                            new LambdaQueryWrapper<DeadlineNode>()
//                                    .eq(DeadlineNode::getRectificationId, rectificationInfo1.getId())
//                                    .orderByDesc(DeadlineNode::getId)
//                    );
//                    deadlineNodeList = filiterNode(rectificationInfo1,deadlineNodeList);
//                    rectificationInfo1 = calcWarnLevel(rectificationInfo1,deadlineNodeList,deadlineRectificationPeriods);
                    if (rectificationInfo1.getWarnLevel() != null) {
                        if (rectificationInfo1.getWarnLevel() == 2) {
                            red++;
                        } else if (rectificationInfo1.getWarnLevel() == 1) {
                            yellow++;
                        } else {
                            green++;
                        }
                    } else {
                        green++;
                    }
                    rectificationV2Vo.setRed(red);
                    rectificationV2Vo.setGreen(green);
                    rectificationV2Vo.setYellow(yellow);
                    rectificationV2Vo.setTotal(red + green + yellow);
                }
                rectificationV2VoList.add(rectificationV2Vo);
            }


            List<String> customOrder = Arrays.asList(
                    "项目建设",
                    "招商引资",
                    "服务企业",
                    "主城崛起-科技城",
                    "主城崛起-空港城",
                    "主城崛起-文旅城",
                    "省市十大民生项目滞后事项",
                    "信访积案化解",
                    "河湖长制",
                    "林长制",
                    "大气污染防治",
                    "打击治理电信网络诈骗",
                    "开展禁毒整治攻坚"
            );
            Map<String, Integer> orderMap = new HashMap<>();
            for (int i = 0; i < customOrder.size(); i++) {
                orderMap.put(customOrder.get(i), i);
            }
            if (rectificationInfo.getProjectType().equals("高质量发展")) {
                // 合并主城崛起的
                rectificationV2VoList = mergeMainCityRise(rectificationV2VoList);
            }
            // 按照自定义顺序排序
            rectificationV2VoList.sort((item1, item2) -> {
                // 获取两个元素的顺序索引
                int index1 = orderMap.getOrDefault(item1.getStreet(), customOrder.size());
                int index2 = orderMap.getOrDefault(item2.getStreet(), customOrder.size());
                return Integer.compare(index1, index2);
            });

        } else {

            List<RectificationInfo> rectificationInfoList = fetchAllRectification(null);

//            List<DeadlineRectificationPeriod> deadlineRectificationPeriods = deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<>());
//
//            for (RectificationInfo rectificationInfoItem : rectificationInfoList) {
//                // ---------------------- 原有currentNode和warnLevel计算逻辑（不变）----------------------
//                List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(
//                        new LambdaQueryWrapper<DeadlineNode>()
//                                .eq(DeadlineNode::getRectificationId, rectificationInfoItem.getId())
//                                .orderByDesc(DeadlineNode::getId)
//                );
//                deadlineNodeList = filiterNode(rectificationInfoItem,deadlineNodeList);
//                RectificationInfo  rectificationInfoItem2 = calcWarnLevel(rectificationInfoItem,deadlineNodeList,deadlineRectificationPeriods);
//                rectificationInfoItem.setWarnLevel(rectificationInfoItem2.getWarnLevel());
//            }
            List<RectificationInfo> warnList = rectificationInfoList.stream().filter(item -> item.getWarnLevel() == 1).collect(Collectors.toList());
            out.println("warnList.size() == " + warnList.size());
            List<String> projectTypeList = Arrays.asList(
                    "高质量发展", "民生保障", "防风险促稳定"
            );
            // 3. 分组：目标区划内的按原区划名分组，不在内的归为“市本级”
            Map<String, List<RectificationInfo>> deptProjectMap = rectificationInfoList.stream()
                    .filter(info -> {
                        // 先过滤无有效区划名的项目（避免空key）
                        String projectType = info.getProjectType();
                        return projectType != null && !projectType.trim().isEmpty();
                    })
                    .collect(Collectors.groupingBy(info -> {
                        // 标准化项目的区划名（去空格，统一匹配标准）
                        String projectType = info.getProjectType().trim();
                        // 判断：若在目标区划列表中，用原区划名；否则归为“市本级”
                        return projectType;
                    }));
            // （可选）若目标区划列表中某个区划无项目，主动添加空列表（避免key缺失）
            for (String targetDept : projectTypeList) {
                deptProjectMap.putIfAbsent(targetDept, new ArrayList<>());
            }
            List<RectificationV2Vo> rectificationV2VoList2 = convert(deptProjectMap);
            // java 固定排序
            List<String> customOrder = Arrays.asList("高质量发展", "民生保障", "防风险促稳定");
            Map<String, Integer> orderMap = new HashMap<>();
            for (int i = 0; i < customOrder.size(); i++) {
                orderMap.put(customOrder.get(i), i);
            }
            // 按照自定义顺序排序
            rectificationV2VoList2.sort((item1, item2) -> {
                // 获取两个元素的顺序索引
                int index1 = orderMap.getOrDefault(item1.getStreet(), customOrder.size());
                int index2 = orderMap.getOrDefault(item2.getStreet(), customOrder.size());
                return Integer.compare(index1, index2);
            });
            rectificationV2VoList = rectificationV2VoList2;
        }
        return Answer.ok(rectificationV2VoList);
    }


    private List<RectificationV2Vo> mergeMainCityRise(List<RectificationV2Vo> originalList) {
        // 空列表直接返回，避免空指针
        if (CollectionUtil.isEmpty(originalList)) {
            return new ArrayList<>();
        }

        // 1. 分组：按“是否为主城崛起子项（主城崛起-XXX）”拆分
        Map<Boolean, List<RectificationV2Vo>> groupMap = originalList.stream()
                .collect(Collectors.partitioningBy(
                        vo -> vo.getStreet() != null && vo.getStreet().startsWith("主城崛起-")
                ));

        // 2. 提取两组数据
        List<RectificationV2Vo> mainCityRiseChildren = groupMap.get(Boolean.TRUE); // 原“主城崛起-空港城”等子项
        List<RectificationV2Vo> otherItems = groupMap.get(Boolean.FALSE);         // 非主城崛起的其他项（如项目建设、招商引资）

        // 3. 构建合并后的“主城崛起”总项
        RectificationV2Vo mergedMainCity = new RectificationV2Vo();
        mergedMainCity.setStreet("主城崛起"); // 合并后的统一名称
        int redSum = 0, greenSum = 0, greenFinishSum = 0, greenNotFinishSum = 0, yellowSum = 0, totalSum = 0;

        // 累加所有主城崛起子项的数值（空值按0处理，避免求和异常）
        if (CollectionUtil.isNotEmpty(mainCityRiseChildren)) {
            for (RectificationV2Vo child : mainCityRiseChildren) {
                redSum += (child.getRed() == null) ? 0 : child.getRed();
                greenSum += (child.getGreen() == null) ? 0 : child.getGreen();
                greenFinishSum += (child.getGreenFinish() == null) ? 0 : child.getGreenFinish();
                greenNotFinishSum += (child.getGreenNotFinish() == null) ? 0 : child.getGreenNotFinish();
                yellowSum += (child.getYellow() == null) ? 0 : child.getYellow();
                totalSum += (child.getTotal() == null) ? 0 : child.getTotal();
            }
        }

        // 给合并项赋值（即使无子项，也设为0避免null）
        mergedMainCity.setRed(redSum);
        mergedMainCity.setGreen(greenSum);
        mergedMainCity.setGreenFinish(greenFinishSum);
        mergedMainCity.setGreenNotFinish(greenNotFinishSum);
        mergedMainCity.setYellow(yellowSum);
        mergedMainCity.setTotal(totalSum);

        // 4. 组装最终结果：其他项 + 合并项（关键：移除原子类项，只保留合并项）
        List<RectificationV2Vo> finalList = new ArrayList<>();
        // 先加非主城崛起的其他项（保持原有顺序）
        if (CollectionUtil.isNotEmpty(otherItems)) {
            finalList.addAll(otherItems);
        }
        // 再加合并后的“主城崛起”项（无论是否有子项，都添加避免漏数据；若需隐藏空数据可加判断）
        finalList.add(mergedMainCity);

        // 5. 验证：打印前后数据量（可选，用于调试）
        out.println("原列表大小：" + originalList.size());
        out.println("合并后列表大小：" + finalList.size());
        out.println("主城崛起子项数量：" + (mainCityRiseChildren == null ? 0 : mainCityRiseChildren.size()));
        out.println("其他项数量：" + (otherItems == null ? 0 : otherItems.size()));

        return finalList;
    }


    @Override
    public Answer<Object> deleteItem(Integer id) {
        int res = 0;
        res = rectificationInfoMapper.deleteById(id);
        return Answer.ok(res);
    }


    @Override
    public Answer<Object> rectificationAnalyzeByStreet() {
        // 1. 获取所有数据
        List<RectificationInfo> rectificationInfoList = fetchAllRectification(null);

        // 2. 批量获取所有相关的数据
        List<Integer> rectificationIds = rectificationInfoList.stream()
                .map(RectificationInfo::getId)
                .collect(Collectors.toList());

        // 批量获取DeadlineRectificationPeriods
        List<DeadlineRectificationPeriod> deadlineRectificationPeriods =
                deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<>());

        // 批量获取DeadlineNodes并按rectificationId分组
        Map<Integer, List<DeadlineNode>> nodeMap = Collections.emptyMap();
        if (CollectionUtil.isNotEmpty(rectificationIds)) {
            List<DeadlineNode> allNodes = deadlineNodeMapper.selectList(
                    new LambdaQueryWrapper<DeadlineNode>()
                            .in(DeadlineNode::getRectificationId, rectificationIds)
                            .orderByDesc(DeadlineNode::getId)
            );

            nodeMap = allNodes.stream()
                    .collect(Collectors.groupingBy(DeadlineNode::getRectificationId));
        }

        // 创建final引用用于Lambda表达式
        final Map<Integer, List<DeadlineNode>> finalNodeMap = nodeMap;
        final List<DeadlineRectificationPeriod> finalDeadlinePeriods = deadlineRectificationPeriods;

        // 3. 使用并行流处理警告级别计算
        rectificationInfoList = rectificationInfoList.parallelStream()
                .map(rectificationInfo1 -> {
                    List<DeadlineNode> deadlineNodeList = finalNodeMap.getOrDefault(
                            rectificationInfo1.getId(), new ArrayList<>());
                    deadlineNodeList = filiterNode(rectificationInfo1, deadlineNodeList);
                    RectificationInfo updatedInfo = calcWarnLevel(
                            rectificationInfo1, deadlineNodeList, finalDeadlinePeriods);

                    rectificationInfo1.setWarnLevel(updatedInfo.getWarnLevel());
                    rectificationInfo1.setCurrentNode(updatedInfo.getCurrentNode());
                    return rectificationInfo1;
                })
                .collect(Collectors.toList());

        // 4. 生成部门项目结构
        RectificationFourAreaClassifier classifier = new RectificationFourAreaClassifier();
        List<RectificationFourAreaClassifier.DepartmentNode> fourLevelTree =
                classifier.generateDepartmentProjectStructure(rectificationInfoList);

        // 5. 过滤空节点
        fourLevelTree = fourLevelTree.stream()
                .filter(project -> project.getTotal() > 0)
                .collect(Collectors.toList());

        // 6. 定义固定区划顺序
        List<String> fixedAreaOrder = Arrays.asList(
                "黄州区", "团风县", "红安县", "麻城市",
                "罗田县", "英山县", "浠水县", "蕲春县",
                "武穴市", "黄梅县", "黄冈高新区","市临空经济区",
                "龙感湖管理区", "白莲河示范区"
        );

        // 7. 自定义排序
        fourLevelTree.sort((deptNode1, deptNode2) -> {
            // 1. 按红灯率升序
            double redRate1 = RectificationFourAreaClassifier.convertRedRateToDouble(deptNode1.getTotalRedRate());
            double redRate2 = RectificationFourAreaClassifier.convertRedRateToDouble(deptNode2.getTotalRedRate());
            int rateCompare = Double.compare(redRate1, redRate2);
            if (rateCompare != 0) {
                return rateCompare;
            }

            // 2. 按固定区划顺序排序
            String area1 = deptNode1.getName() != null ? deptNode1.getName() : "";
            String area2 = deptNode2.getName() != null ? deptNode2.getName() : "";

            int index1 = fixedAreaOrder.indexOf(area1);
            int index2 = fixedAreaOrder.indexOf(area2);

            // 如果都在固定列表中，按列表顺序排序
            if (index1 != -1 && index2 != -1) {
                return Integer.compare(index1, index2);
            }

            // 如果只有一个在固定列表中，固定列表中的排在前面
            if (index1 != -1) {
                return -1;
            }
            if (index2 != -1) {
                return 1;
            }

            // 如果都不在固定列表中，按字母顺序排序
            return area1.compareTo(area2);
        });

        return Answer.ok(fourLevelTree);
    }

//    @Override
//    public Answer<Object> rectificationAnalyzeByStreet() {
//        // 原有 departments 列表不变...
//        List<String> departments = Arrays.asList(
//                "黄州区", "团风县", "红安县", "麻城市",
//                "罗田县", "英山县", "浠水县", "蕲春县",
//                "武穴市", "黄梅县", "龙感湖管理区", "白莲河示范区",
//                "黄冈高新区","市临空经济区"
//        );
//        List<RectificationAnalyze> rectificationAnalyzeList = new ArrayList<>();
//
//        LambdaQueryWrapper<RectificationInfo> rectificationInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
//        rectificationInfoLambdaQueryWrapper.ne(RectificationInfo::getState, "已关闭");
//        rectificationInfoLambdaQueryWrapper.in(RectificationInfo::getDepartmentName, departments);
//        List<RectificationInfo> rectificationInfoList = rectificationInfoMapper.selectList(rectificationInfoLambdaQueryWrapper);
//
//
//        List<CompanyData> companyList = companyDataMapper.selectList(new LambdaQueryWrapper<>());
//        for (int i = 0; i < companyList.size(); i++) {
//            RectificationInfo info2 = new RectificationInfo();
//            info2.setProjectType("高质量发展");
//            info2.setProjectChildType("服务企业");
//            info2.setProjectChildCategoryType("重点工业企业包保服务");
//            info2.setDepartmentName(companyList.get(i).getDepartmentName());
//            if (companyList.get(i).getLampStatus() .equals("1")){
//                info2.setWarnLevel(0);
//            }else if (companyList.get(i).getLampStatus() .equals("2")){
//                info2.setWarnLevel(2);
//            }else{
//                info2.setWarnLevel(1);
//            }
//            rectificationInfoList.add(info2);
//        }
//
//        Map<String, List<RectificationInfo>> rectificationInfoMap = new HashMap<>();
//        if (CollectionUtil.isNotEmpty(rectificationInfoList)) { // 优化：用hutool工具类判断非空
//            rectificationInfoMap = rectificationInfoList.stream()
//                    .filter(x -> x.getDepartmentName() != null)
//                    .collect(Collectors.groupingBy(RectificationInfo::getDepartmentName));
//        }
//
//        for (String key : rectificationInfoMap.keySet()) {
//            List<RectificationInfo> rectificationInfos = rectificationInfoMap.get(key);
//            List<RectificationInfo> rectificationInfoListG = new ArrayList<>();
//            List<RectificationInfo> rectificationInfoListM = new ArrayList<>();
//            List<RectificationInfo> rectificationInfoListF = new ArrayList<>();
//
//            // 1. 按项目类型分组（原有逻辑不变）
//            for (RectificationInfo rectificationInfo : rectificationInfos) {
//                if ("高质量发展".equals(rectificationInfo.getProjectType())) {
//                    rectificationInfoListG.add(rectificationInfo);
//                } else if ("民生保障".equals(rectificationInfo.getProjectType())) {
//                    rectificationInfoListM.add(rectificationInfo);
//                } else if ("防风险促稳定".equals(rectificationInfo.getProjectType())) {
//                    rectificationInfoListF.add(rectificationInfo);
//                }
//            }
//
//            // 2. 处理“总数”类型的 RectificationAnalyze（重点：补充 red 字段赋值）
//            RectificationV2Vo rectificationV2Vo1 = calculateRate(rectificationInfos);
//
//            System.out.println(rectificationV2Vo1);
//
//            RectificationAnalyze rectificationAnalyzeZ = new RectificationAnalyze();
//            rectificationAnalyzeZ.setName(key);
//            rectificationAnalyzeZ.setProjectType("总数");
//
//            if (rectificationV2Vo1 != null) {
//                int total = rectificationV2Vo1.getTotal() == null ? 0 : rectificationV2Vo1.getTotal();
//                int red = rectificationV2Vo1.getRed() == null ? 0 : rectificationV2Vo1.getRed(); // 从V2Vo获取红灯数
//
//                rectificationAnalyzeZ.setTotal(total);
//                rectificationAnalyzeZ.setRed(red); // 赋值红灯数（关键）
//
//                // 计算红灯率（原有逻辑不变）
//                if (total != 0) {
//                    rectificationAnalyzeZ.setRedRate(String.format("%.2f%%", (double) red / total * 100));
//                } else {
//                    rectificationAnalyzeZ.setRedRate("0%");
//                }
//
//                // 计算绿灯率（原有逻辑不变，可按需补充 green 字段，类似 red）
//                int green = rectificationV2Vo1.getGreen() == null ? 0 : rectificationV2Vo1.getGreen();
//                if (total != 0) {
//                    rectificationAnalyzeZ.setGreenRate(String.format("%.2f%%", (double) green / total * 100));
//                } else {
//                    rectificationAnalyzeZ.setGreenRate("0%");
//                }
//            } else {
//                rectificationAnalyzeZ.setTotal(0);
//                rectificationAnalyzeZ.setRed(0); // 空数据时红灯数设为0
//                rectificationAnalyzeZ.setGreenRate("0%");
//                rectificationAnalyzeZ.setRedRate("0%");
//            }
//            rectificationAnalyzeList.add(rectificationAnalyzeZ);
//
//            // 3. 处理“高质量发展”类型（重点：补充 red 字段赋值）
//            Map<String, List<RectificationInfo>> rectificationInfoGCMap = new HashMap<>();
//            if (CollectionUtil.isNotEmpty(rectificationInfoListG)) {
//                rectificationInfoGCMap = rectificationInfoListG.stream()
//                        .filter(x -> x.getDepartmentName() != null)
//                        .collect(Collectors.groupingBy(RectificationInfo::getProjectChildCategoryType));
//            }
//            for (String keyG : rectificationInfoGCMap.keySet()) {
//                List<RectificationInfo> rectificationInfos1 = rectificationInfoGCMap.get(keyG);
//                RectificationV2Vo rectificationV2Vo = calculateRate(rectificationInfos1);
//                RectificationAnalyze rectificationAnalyze = new RectificationAnalyze();
//                rectificationAnalyze.setName(key);
//                rectificationAnalyze.setProjectType("高质量发展");
//                rectificationAnalyze.setProjectChildCategoryType(keyG);
//
//                if (rectificationV2Vo != null) {
//                    int total = rectificationV2Vo.getTotal() == null ? 0 : rectificationV2Vo.getTotal();
//                    int red = rectificationV2Vo.getRed() == null ? 0 : rectificationV2Vo.getRed(); // 获取红灯数
//
//                    rectificationAnalyze.setTotal(total);
//                    rectificationAnalyze.setRed(red); // 赋值红灯数（关键）
//
//                    // 计算红灯率（原有逻辑不变）
//                    if (total != 0) {
//                        rectificationAnalyze.setRedRate(String.format("%.2f%%", (double) red / total * 100));
//                    } else {
//                        rectificationAnalyze.setRedRate("0%");
//                    }
//
//                    // 计算绿灯率（原有逻辑不变）
//                    int green = rectificationV2Vo.getGreen() == null ? 0 : rectificationV2Vo.getGreen();
//                    if (total != 0) {
//                        rectificationAnalyze.setGreenRate(String.format("%.2f%%", (double) green / total * 100));
//                    } else {
//                        rectificationAnalyze.setGreenRate("0%");
//                    }
//                } else {
//                    rectificationAnalyze.setTotal(0);
//                    rectificationAnalyze.setRed(0); // 空数据时红灯数设为0
//                    rectificationAnalyze.setGreenRate("0%");
//                    rectificationAnalyze.setRedRate("0%");
//                }
//                rectificationAnalyzeList.add(rectificationAnalyze);
//            }
//
//            // 4. 处理“民生保障”类型（重点：补充 red 字段赋值，逻辑同“高质量发展”）
//            Map<String, List<RectificationInfo>> rectificationInfoMCMap = new HashMap<>();
//            if (CollectionUtil.isNotEmpty(rectificationInfoListM)) { // 修复：原代码判断的是 rectificationInfoMCMap，改为判断 listM
//                rectificationInfoMCMap = rectificationInfoListM.stream()
//                        .filter(x -> x.getDepartmentName() != null)
//                        .collect(Collectors.groupingBy(RectificationInfo::getProjectChildType));
//            }
//            for (String keyG : rectificationInfoMCMap.keySet()) {
//                List<RectificationInfo> rectificationInfos1 = rectificationInfoMCMap.get(keyG);
//                RectificationV2Vo rectificationV2Vo = calculateRate(rectificationInfos1);
//                RectificationAnalyze rectificationAnalyze = new RectificationAnalyze();
//                rectificationAnalyze.setName(key);
//                rectificationAnalyze.setProjectType("民生保障");
//                rectificationAnalyze.setProjectChildCategoryType(keyG);
//
//                if (rectificationV2Vo != null) {
//                    int total = rectificationV2Vo.getTotal() == null ? 0 : rectificationV2Vo.getTotal();
//                    int red = rectificationV2Vo.getRed() == null ? 0 : rectificationV2Vo.getRed(); // 获取红灯数
//
//                    rectificationAnalyze.setTotal(total);
//                    rectificationAnalyze.setRed(red); // 赋值红灯数（关键）
//
//                    if (total != 0) {
//                        rectificationAnalyze.setRedRate(String.format("%.2f%%", (double) red / total * 100));
//                    } else {
//                        rectificationAnalyze.setRedRate("0%");
//                    }
//
//                    int green = rectificationV2Vo.getGreen() == null ? 0 : rectificationV2Vo.getGreen();
//                    if (total != 0) {
//                        rectificationAnalyze.setGreenRate(String.format("%.2f%%", (double) green / total * 100));
//                    } else {
//                        rectificationAnalyze.setGreenRate("0%");
//                    }
//                } else {
//                    rectificationAnalyze.setTotal(0);
//                    rectificationAnalyze.setRed(0); // 空数据时红灯数设为0
//                    rectificationAnalyze.setGreenRate("0%");
//                    rectificationAnalyze.setRedRate("0%");
//                }
//                rectificationAnalyzeList.add(rectificationAnalyze);
//            }
//
//            // 5. 处理“防风险促稳定”类型（重点：补充 red 字段赋值，逻辑同上）
//            Map<String, List<RectificationInfo>> rectificationInfoFCMap = new HashMap<>();
//            if (CollectionUtil.isNotEmpty(rectificationInfoListF)) {
//                rectificationInfoFCMap = rectificationInfoListF.stream()
//                        .filter(x -> x.getDepartmentName() != null)
//                        .collect(Collectors.groupingBy(RectificationInfo::getProjectChildType));
//            }
//            for (String keyG : rectificationInfoFCMap.keySet()) {
//                List<RectificationInfo> rectificationInfos1 = rectificationInfoFCMap.get(keyG);
//                RectificationV2Vo rectificationV2Vo = calculateRate(rectificationInfos1);
//                RectificationAnalyze rectificationAnalyze = new RectificationAnalyze();
//                rectificationAnalyze.setName(key);
//                rectificationAnalyze.setProjectType("防风险促稳定");
//                rectificationAnalyze.setProjectChildCategoryType(keyG);
//
//                if (rectificationV2Vo != null) {
//                    int total = rectificationV2Vo.getTotal() == null ? 0 : rectificationV2Vo.getTotal();
//                    int red = rectificationV2Vo.getRed() == null ? 0 : rectificationV2Vo.getRed(); // 获取红灯数
//
//                    rectificationAnalyze.setTotal(total);
//                    rectificationAnalyze.setRed(red); // 赋值红灯数（关键）
//
//                    if (total != 0) {
//                        rectificationAnalyze.setRedRate(String.format("%.2f%%", (double) red / total * 100));
//                    } else {
//                        rectificationAnalyze.setRedRate("0%");
//                    }
//
//                    int green = rectificationV2Vo.getGreen() == null ? 0 : rectificationV2Vo.getGreen();
//                    if (total != 0) {
//                        rectificationAnalyze.setGreenRate(String.format("%.2f%%", (double) green / total * 100));
//                    } else {
//                        rectificationAnalyze.setGreenRate("0%");
//                    }
//                } else {
//                    rectificationAnalyze.setTotal(0);
//                    rectificationAnalyze.setRed(0); // 空数据时红灯数设为0
//                    rectificationAnalyze.setGreenRate("0%");
//                    rectificationAnalyze.setRedRate("0%");
//                }
//                rectificationAnalyzeList.add(rectificationAnalyze);
//            }
//        }
//
//        List<RegionTotalVO> regionProjectVOS = mergeProjectByRegion(rectificationAnalyzeList);
//
//
//        regionProjectVOS.sort((r1, r2) -> {
//            // 获取两个对象的红灯率字符串
//            String rate1 = r1.getTotalRedRate();
//            String rate2 = r2.getTotalRedRate();
//
//            // 处理空值情况：空值排在最后
//            if (rate1 == null || rate1.trim().isEmpty()) {
//                return 1; // r1空，r1排在r2后面
//            }
//            if (rate2 == null || rate2.trim().isEmpty()) {
//                return -1; // r2空，r1排在r2前面
//            }
//
//            try {
//                // 提取百分比数值（去除百分号并转换为double）
//                double value1 = Double.parseDouble(rate1.trim().replace("%", ""));
//                double value2 = Double.parseDouble(rate2.trim().replace("%", ""));
//
//                // 按数值升序排序（由低到高）
//                return Double.compare(value1, value2);
//            } catch (NumberFormatException e) {
//                // 处理格式错误：格式错误的排在后面
//                // 如果两个都格式错误，保持相对顺序
//                return 0;
//            }
//        });
//        return Answer.ok(regionProjectVOS);
//    }

    /**
     * 排序RegionTotalVO列表：市本级固定首位，剩余按totalGreenRate从低到高排序
     *
     * @param regionProjectVOS 原始列表
     * @return 排序后的列表
     */
    public static List<RectificationV2Vo> sortRegionList(List<RectificationV2Vo> regionProjectVOS) {
        // 1. 处理空列表，避免空指针
        if (CollectionUtil.isEmpty(regionProjectVOS)) {
            return new ArrayList<>();
        }

        // 2. 拆分列表：市本级 与 其他区域
        RectificationV2Vo cityLevel = null; // 存储市本级条目
        List<RectificationV2Vo> otherRegions = new ArrayList<>(); // 存储其他区域条目

        for (RectificationV2Vo region : regionProjectVOS) {
            // 匹配“市本级”（注意：此处名称需与实际数据一致，若为“市本级”“市本级区域”等需调整）
            if ("市本级".equals(region.getStreet())) {
                cityLevel = region; // 找到市本级，赋值后跳出循环（假设仅1个市本级）
            } else {
                otherRegions.add(region); // 非市本级，加入其他列表
            }
        }
        // 3. 对其他区域按 totalGreenRate 从低到高排序
        List<RectificationV2Vo> sortedOtherRegions = otherRegions.stream()
                // 先通过 convertGreenRateToDouble 提取绿灯率数值，再用 reversed() 改为倒序
                .sorted(Comparator.comparingDouble(RectificationInfoServiceImpl::convertGreenRateToDouble))
                .collect(Collectors.toList());
        // 4. 组装最终结果：市本级（若存在）+ 排序后的其他区域
        List<RectificationV2Vo> finalSortedList = new ArrayList<>();
        if (cityLevel != null) {
            finalSortedList.add(cityLevel); // 市本级固定首位
        }
        finalSortedList.addAll(sortedOtherRegions); // 追加排序后的其他区域

        return finalSortedList;
    }


    // 工具方法：计算单个 RectificationV2Vo 的绿灯率（格式：保留2位小数的百分比字符串）
    private static void calculateTotalGreenRate(RectificationV2Vo vo) {
        if (vo == null || vo.getTotal() == null || vo.getTotal() == 0 || vo.getGreen() == null) {
            vo.setTotalGreenRate("0.0%"); // 总数为0或绿灯数为空时，默认绿灯率0%
            return;
        }
        // 计算绿灯率：(绿灯数 / 总数) * 100，保留2位小数
        BigDecimal red = new BigDecimal(vo.getRed());
        BigDecimal total = new BigDecimal(vo.getTotal());
        BigDecimal redRate = red.divide(total, 4, RoundingMode.HALF_UP) // 先保留4位小数（如 0.3050）
                .multiply(new BigDecimal(100)) // 转为百分比（如 30.50）
                .setScale(1, RoundingMode.HALF_UP); // 最终保留2位小数
        vo.setTotalGreenRate(redRate + "%"); // 格式化为 "30.50%"
        if (vo.getRed828() != null) {
            BigDecimal red828 = new BigDecimal(vo.getRed828());
            if (red.compareTo(BigDecimal.ZERO) != 0) {
                BigDecimal weight828 = red828.divide(red, 4, RoundingMode.HALF_UP)
                        .multiply(new BigDecimal(100))
                        .setScale(1, RoundingMode.HALF_UP);
                vo.setWeight828(weight828 + "%");
            } else {
                vo.setWeight828("0.0%");
            }
        }
    }

    // 修复：解析绿灯率字符串为 Double（用于排序）
    private static double convertGreenRateToDouble(RectificationV2Vo vo) {
        try {
            String greenRateStr = vo.getTotalGreenRate();
            if (greenRateStr == null || greenRateStr.trim().isEmpty()) {
                return 0.0; // 空值默认0%
            }
            // 去除百分号，转为Double（如 "30.50%" → 30.50）
            return new BigDecimal(greenRateStr.replace("%", "")).doubleValue();
        } catch (NumberFormatException e) {
            return 0.0; // 格式错误默认0%
        }
    }

    // 核心业务方法：先计算绿灯率，再排序
    public List<RectificationV2Vo> processAndSortRegionList(List<RectificationV2Vo> originalList) {
        if (CollectionUtil.isEmpty(originalList)) {
            return new ArrayList<>();
        }
        // 第一步：遍历列表，为每个条目计算绿灯率（totalGreenRate）
        for (RectificationV2Vo vo : originalList) {
            calculateTotalGreenRate(vo);
        }
        // 第二步：调用原有排序方法，此时 totalGreenRate 已存在，不会报错
        return sortRegionList(originalList);
    }

    /**
     * 按责任人统计,项目的和环节的,只要数量
     *
     * @return
     */
    @Override
    public Answer<Object> rectificationAnalyzeByPeople() {
        List<RectificationInfo> rectificationInfoList = fetchAllRectification("");
        rectificationInfoList = RectificationClassifier.SplitPerson(rectificationInfoList);
        List<DeadlineNode> originDeadLineNodeList = new ArrayList<>();

        // 空集合判断，提前返回避免无效操作
        // 1. 收集所有需要查询的 rectificationId（假设为Integer类型）
        List<Integer> rectificationIds = rectificationInfoList.stream()
                .map(RectificationInfo::getId)
                .collect(Collectors.toList());

        // 2. 批量查询所有关联的 DeadlineNode（1次SQL替代N次循环SQL）
        List<DeadlineNode> allDeadlineNodes = deadlineNodeMapper.selectList(
                new LambdaQueryWrapper<DeadlineNode>()
                        .in(DeadlineNode::getRectificationId, rectificationIds) // 批量匹配
                        .orderByDesc(DeadlineNode::getId) // 保持原有排序
        );

        // 3. 按 rectificationId 分组，方便内存匹配（键为Integer类型）
        Map<Integer, List<DeadlineNode>> nodeGroupByRectId = allDeadlineNodes.stream()
                .collect(Collectors.groupingBy(DeadlineNode::getRectificationId));

        // 4. 遍历 rectificationInfoList，处理并收集节点（纯内存操作）
        for (RectificationInfo rectInfo : rectificationInfoList) {
            Integer rectId = rectInfo.getId();
            // 从分组中获取当前rectificationId对应的节点列表（无则返回空集合）
            List<DeadlineNode> deadlineNodeList = nodeGroupByRectId.getOrDefault(rectId, new ArrayList<>());

            // 原有过滤逻辑不变
            deadlineNodeList = filiterNode(rectInfo, deadlineNodeList);

            // 收集处理后的节点
            originDeadLineNodeList.addAll(deadlineNodeList);
        }

        //拆分项目的责任人生成的一个新的数组
        List<RectificationInfo> processedList = RectificationClassifier.SplitPerson(rectificationInfoList);

        List<DeadlineNode> processNodeList = RectificationClassifier.SplitNodePerson(originDeadLineNodeList);


        // 责任人合并统计
        List<RectificationPersonStatistics> personStaticList = RectificationPersonStatisticsUtil.statistics(processedList, processNodeList);

        List<ReminderPerson> reminderPeople = reminderPersonMapper.selectList(new LambdaQueryWrapper<ReminderPerson>());

        // 构建姓名到手机号的映射表
        Map<String, ReminderPerson> namePhoneMap = new HashMap<>(reminderPeople.size());
        for (ReminderPerson rp : reminderPeople) {
            if (rp.getPersonName() != null && rp.getPhone() != null) {
                // 去除姓名前后空格，避免因空格导致匹配失败
                String personName = rp.getPersonName().trim();
                namePhoneMap.put(personName, rp);
            }
        }

        // 为统计列表中的每个对象设置手机号
        for (RectificationPersonStatistics stats : personStaticList) {
            if (stats.getPersonLiable() != null) {
                String personName = stats.getPersonLiable().trim();
                // 从映射表中获取手机号并设置
                if (namePhoneMap.get(personName) != null) {
                    String phone = namePhoneMap.get(personName).getPhone();
                    String department = namePhoneMap.get(personName).getUnitName();
                    stats.setPhone(phone); // 假设存在setPhone方法
                    stats.setDepartment(department); // 假设存在setPhone方法
                }
            }
        }
        personStaticList = personStaticList.stream().filter(item -> item.getPhone() != null).collect(Collectors.toList());
        return Answer.ok(personStaticList);

    }


    @Override
    public Answer<Object> rectificationAnalyzeByPeople2() {
        List<RectificationInfo> rectificationInfoList = fetchAllRectification("");
        rectificationInfoList = RectificationClassifier.SplitPerson(rectificationInfoList);
        List<DeadlineNode> originDeadLineNodeList = new ArrayList<>();

        // 空集合判断，提前返回避免无效操作
        // 1. 收集所有需要查询的 rectificationId（假设为Integer类型）
        List<Integer> rectificationIds = rectificationInfoList.stream()
                .map(RectificationInfo::getId)
                .collect(Collectors.toList());

        // 2. 批量查询所有关联的 DeadlineNode（1次SQL替代N次循环SQL）
        List<DeadlineNode> allDeadlineNodes = deadlineNodeMapper.selectList(
                new LambdaQueryWrapper<DeadlineNode>()
                        .in(DeadlineNode::getRectificationId, rectificationIds) // 批量匹配
                        .orderByDesc(DeadlineNode::getId) // 保持原有排序
        );

        // 3. 按 rectificationId 分组，方便内存匹配（键为Integer类型）
        Map<Integer, List<DeadlineNode>> nodeGroupByRectId = allDeadlineNodes.stream()
                .collect(Collectors.groupingBy(DeadlineNode::getRectificationId));

        // 4. 遍历 rectificationInfoList，处理并收集节点（纯内存操作）
        for (RectificationInfo rectInfo : rectificationInfoList) {
            Integer rectId = rectInfo.getId();
            // 从分组中获取当前rectificationId对应的节点列表（无则返回空集合）
            List<DeadlineNode> deadlineNodeList = nodeGroupByRectId.getOrDefault(rectId, new ArrayList<>());

            // 原有过滤逻辑不变
            deadlineNodeList = filiterNode(rectInfo, deadlineNodeList);

            // 收集处理后的节点
            originDeadLineNodeList.addAll(deadlineNodeList);
        }

        //拆分项目的责任人生成的一个新的数组
        List<RectificationInfo> processedList = RectificationClassifier.SplitPerson(rectificationInfoList);

        List<DeadlineNode> processNodeList = RectificationClassifier.SplitNodePerson(originDeadLineNodeList);


        // 责任人合并统计
        List<RectificationPersonStatistics> personStaticList =
                RectificationPersonStatisticsUtil.statistics(processedList, processNodeList);

        List<ReminderPerson> reminderPeople = reminderPersonMapper.selectList(new LambdaQueryWrapper<ReminderPerson>());

        // 构建姓名到手机号的映射表
        Map<String, ReminderPerson> namePhoneMap = new HashMap<>(reminderPeople.size());
        for (ReminderPerson rp : reminderPeople) {
            if (rp.getPersonName() != null && rp.getPhone() != null) {
                // 去除姓名前后空格，避免因空格导致匹配失败
                String personName = rp.getPersonName().trim();
                namePhoneMap.put(personName, rp);
            }
        }

        // 为统计列表中的每个对象设置手机号
        for (RectificationPersonStatistics stats : personStaticList) {
            if (stats.getPersonLiable() != null) {
                String personName = stats.getPersonLiable().trim();
                // 从映射表中获取手机号并设置
                if (namePhoneMap.get(personName) != null) {
                    String phone = namePhoneMap.get(personName).getPhone();
                    String department = namePhoneMap.get(personName).getUnitName();
                    stats.setPhone(phone); // 假设存在setPhone方法
                    stats.setDepartment(department); // 假设存在setPhone方法
                }
            }
        }

        personStaticList = personStaticList.stream().filter(item -> item.getPhone() != null).collect(Collectors.toList());

        // 过滤县市区外的部门 - 修改为检查对象的department属性
        personStaticList = personStaticList.stream()
                .filter(person -> DISTRICTS.contains(person.getDepartment()))
                .collect(Collectors.toList());

        // 按照DISTRICTS中的顺序排序
        personStaticList.sort(Comparator.comparingInt(person -> DISTRICTS.indexOf(person.getDepartment())));

        return Answer.ok(personStaticList);

    }


    // 区划列表
    private static final List<String> DISTRICTS = Arrays.asList(
            "黄州区", "团风县", "红安县", "麻城市",
            "罗田县", "英山县", "浠水县", "蕲春县",
            "武穴市", "黄梅县", "黄冈高新区","市临空经济区",
            "龙感湖管理区", "白莲河示范区"
    );

    public static final List<String> DEPARTMENTS = Arrays.asList(
            "市纪委监委机关", "市委组织部", "市委社工部", "市委政法委",
            "市发改委", "市招商服务中心", "市交通运输局", "市资建局",
            "市城管执法委", "市生态环境局", "市水利和湖泊局", "市林业局",
            "市住新局", "市公安局", "市信访局", "市政数局",
            "市应急管理局", "黄冈国投集团", "市经信局", "市教育局", "市科技局",
            "市民政局", "市司法局", "市财政局", "市人力资源和社会保障局", "市农业农村局",
            "市商务局", "市文旅局", "市卫健委", "市政府国资委", "市市场监督局",
            "市机关事务中心", "市公共资源交易中心", "市公共检验检测中心", "市供销社",
            "市体育发展中心", "市国资委", "市城管委"
    );


    /**
     * 按责任人统计,项目的和环节的,只要数量
     *
     * @return
     */
    @Override
    public Answer<Object> rectificationAnalyzeByArea() {
        List<RectificationInfo> rectificationInfoList = fetchAllRectification("");
        rectificationInfoList = RectificationClassifier.processArea(rectificationInfoList);
        List<DeadlineNode> originDeadLineNodeList = new ArrayList<>();
        // 1. 收集所有需要查询的 rectificationId（假设为Integer类型）
        List<Integer> rectificationIds = rectificationInfoList.stream()
                .map(RectificationInfo::getId)
                .collect(Collectors.toList());

        // 2. 批量查询所有关联的 DeadlineNode（1次SQL替代N次循环查询）
        List<DeadlineNode> allDeadlineNodes = deadlineNodeMapper.selectList(
                new LambdaQueryWrapper<DeadlineNode>()
                        .in(DeadlineNode::getRectificationId, rectificationIds) // 批量匹配ID
                        .orderByDesc(DeadlineNode::getId) // 保持原有排序
        );

        // 3. 按 rectificationId 分组，方便内存快速匹配（键为Integer类型）
        Map<Integer, List<DeadlineNode>> nodeGroupByRectId = allDeadlineNodes.stream()
                .collect(Collectors.groupingBy(DeadlineNode::getRectificationId));

        // 4. 遍历 rectificationInfoList，处理节点（纯内存操作）
        for (RectificationInfo rectInfo : rectificationInfoList) {
            Integer rectId = rectInfo.getId();
            // 从分组中获取当前ID对应的节点列表（无则返回空集合）
            List<DeadlineNode> deadlineNodeList = nodeGroupByRectId.getOrDefault(rectId, new ArrayList<>());

            // 原有过滤逻辑
            deadlineNodeList = filiterNode(rectInfo, deadlineNodeList);

            // 为节点设置部门名称（替换原for循环赋值逻辑，用增强for更简洁）
            for (DeadlineNode node : deadlineNodeList) {
                node.setDepartmentName(rectInfo.getDepartmentName());
            }
            // 收集处理后的节点
            originDeadLineNodeList.addAll(deadlineNodeList);
        }

        //拆分项目的责任人生成的一个新的数组
        List<RectificationInfo> processedList = RectificationClassifier.SplitDepartment(rectificationInfoList);
        // 部门合并统计
        List<RectificationPersonStatistics> personStaticList =
                RectificationDepartmentStatisticsUtil.statistics(processedList, originDeadLineNodeList);


        List<ReminderPerson> reminderPeople = reminderPersonMapper.selectList(new LambdaQueryWrapper<ReminderPerson>());

        // 构建姓名到手机号的映射表
        Map<String, ReminderPerson> namePhoneMap = new HashMap<>(reminderPeople.size());
        for (ReminderPerson rp : reminderPeople) {
            if (rp.getPersonName() != null && rp.getPhone() != null) {
                // 去除姓名前后空格，避免因空格导致匹配失败
                String department = rp.getUnitName().trim();
                namePhoneMap.put(department, rp);
            }
        }

        // 为统计列表中的每个对象设置手机号
        for (RectificationPersonStatistics stats : personStaticList) {
            if (stats.getPersonLiable() != null) {
                String department = stats.getDepartment().trim();
                // 从映射表中获取手机号并设置
                if (namePhoneMap.get(department) != null) {
                    String phone = namePhoneMap.get(department).getPhone();
                    String name = namePhoneMap.get(department).getPersonName();
                    stats.setPhone(phone); // 假设存在setPhone方法
                    stats.setPersonLiable(name); // 假设存在setPhone方法
                }
            }
        }
        personStaticList = personStaticList.stream().filter(item -> item.getPhone() != null).collect(Collectors.toList());


        return Answer.ok(personStaticList);

    }

    @Override
    public Answer<Object> getRectificationInfoById(JSONObject reqJson) {
        Integer id = reqJson.getInteger("id");
        if (id == null) {
            return Answer.error("项目id不能为空");
        }
        RectificationInfo rectificationInfo = rectificationInfoMapper.selectById(id);
        return Answer.ok(rectificationInfo);
    }

    @Override
    public Answer<Object> getRectificationListByDeadLine() {
        List<RectificationInfo> rectificationInfoList = fetchAllRectification("");
        List<DeadlineNode> deadlineNodes = new ArrayList<>();
        List<Integer> rectificationIdList = new ArrayList<>();


        List<DeadlineRectificationPeriod> deadlineRectificationPeriods = deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<DeadlineRectificationPeriod>()
                .eq(DeadlineRectificationPeriod::getIsApproval, 0)
                .orderByDesc(DeadlineRectificationPeriod::getId));


        if (rectificationInfoList != null && !rectificationInfoList.isEmpty()) {
            if (deadlineRectificationPeriods != null && !deadlineRectificationPeriods.isEmpty()) {
                Set<Integer> addedIds = new HashSet<>();
                for (DeadlineRectificationPeriod node : deadlineRectificationPeriods) {
                    DeadlineNode deadlineNode = deadlineNodeMapper.selectById(node.getDeadlineNodeId());
                    if (deadlineNode != null && addedIds.add(deadlineNode.getId())) {
                        deadlineNodes.add(deadlineNode);
                    }
                }
            }
            rectificationIdList = deadlineNodes.stream()
                    .filter(node -> node.getRectificationId() != null)
                    .map(DeadlineNode::getRectificationId)
                    .distinct()
                    .collect(Collectors.toList());
        }

        if (rectificationIdList != null && !rectificationIdList.isEmpty()) {
            List<Integer> safeIdList = rectificationIdList != null ? rectificationIdList : Collections.emptyList();
            rectificationInfoList = rectificationInfoList.stream()
                    .filter(Objects::nonNull)
                    .filter(item -> item.getId() != null && safeIdList.contains(item.getId()))
                    .collect(Collectors.toList());
            return Answer.ok(rectificationInfoList);
        } else {
            return Answer.ok("没有需要审核数据");
        }
    }

    @Override
    public Answer<Object> getRectificationListByUpdateDeadline(JSONObject reqJson) {
        String currentDepartment = reqJson.getString("currentDepartment");
        List<RectificationInfo> rectificationInfoList = fetchAllRectification(currentDepartment);
        List<DeadlineNode> deadlineNodes = new ArrayList<>();
        List<Integer> rectificationIdList = new ArrayList<>();

        List<DeadlineNodeUpdate> list = deadlineNodeUpdateMapper.selectList(
                new LambdaQueryWrapper<DeadlineNodeUpdate>()
                        .isNull(DeadlineNodeUpdate::getApprovalStatus)
                        .orderByDesc(DeadlineNodeUpdate::getId)
        );

        if (rectificationInfoList != null && !rectificationInfoList.isEmpty()) {
            if (list != null && !list.isEmpty()) {
                // 1. 先收集所有需要查询的nodeId（保留原顺序的去重，避免重复查询，但记录所有出现的nodeId）
                List<Integer> nodeIdList = new ArrayList<>();
                Set<Integer> tempNodeIds = new HashSet<>();
                for (DeadlineNodeUpdate node : list) {
                    Integer nodeId = Integer.valueOf(node.getNodeId());
                    if (nodeId != null && tempNodeIds.add(nodeId)) { // 去重但保留首次出现的顺序
                        nodeIdList.add(nodeId);
                    }
                }

                // 2. 批量查询所有nodeId对应的DeadlineNode（1次SQL）
                Map<Integer, DeadlineNode> nodeMap = new HashMap<>();
                if (!nodeIdList.isEmpty()) {
                    List<DeadlineNode> allNodes = deadlineNodeMapper.selectBatchIds(nodeIdList);
                    // 用map缓存查询结果，key为nodeId
                    for (DeadlineNode node : allNodes) {
                        if (node != null) {
                            nodeMap.put(node.getId(), node); // 假设node.getId()就是nodeId（与node.getNodeId()一致）
                        }
                    }
                }

                // 3. 按原list的遍历顺序处理，保持原去重逻辑
                Set<Integer> addedIds = new HashSet<>();
                for (DeadlineNodeUpdate node : list) {
                    Integer nodeId = Integer.valueOf(node.getNodeId());
                    if (nodeId == null) {
                        continue;
                    }
                    // 从缓存中获取批量查询的结果，模拟原查询逻辑
                    DeadlineNode deadlineNode = nodeMap.get(nodeId);
                    // 与原逻辑完全一致的判断：查询到有效节点，且节点id未被添加过
                    if (deadlineNode != null && addedIds.add(deadlineNode.getId())) {
                        deadlineNodes.add(deadlineNode);
                    }
                }
            }

            // 提取rectificationIdList的逻辑不变
            rectificationIdList = deadlineNodes.stream()
                    .filter(node -> node.getRectificationId() != null)
                    .map(DeadlineNode::getRectificationId)
                    .distinct()
                    .collect(Collectors.toList());
        }

//        if (rectificationInfoList!=null&&!rectificationInfoList.isEmpty()) {
//            if (list != null && !list.isEmpty()) {
//                Set<Integer> addedIds = new HashSet<>();
//                for (DeadlineNodeUpdate node : list) {
//                    DeadlineNode deadlineNode = deadlineNodeMapper.selectById(node.getNodeId());
//                    if (deadlineNode != null && addedIds.add(deadlineNode.getId())) {
//                        deadlineNodes.add(deadlineNode);
//                    }
//                }
//            }
//            rectificationIdList = deadlineNodes.stream()
//                    .filter(node -> node.getRectificationId() != null)
//                    .map(DeadlineNode::getRectificationId)
//                    .distinct()
//                    .collect(Collectors.toList());
//        }

        if (rectificationIdList != null && !rectificationIdList.isEmpty()) {
            List<Integer> safeIdList = rectificationIdList != null ? rectificationIdList : Collections.emptyList();
            rectificationInfoList = rectificationInfoList.stream()
                    .filter(Objects::nonNull)
                    .filter(item -> item.getId() != null && safeIdList.contains(item.getId()))
                    .filter(item -> currentDepartment.equals(item.getCreatorUnit()))
                    .collect(Collectors.toList());
            return Answer.ok(rectificationInfoList);
        } else {
            return Answer.ok("没有需要审核数据");
        }
    }


    // 常量定义（建议放在类级别，方便复用）
    private static final String CITY_LEVEL = "市本级";
    private static final int COUNTY_DISTRICT_COUNT = 10;
    private static final int FUNCTIONAL_AREA_COUNT = 4;

    private static final List<String> AREA_ORDER = Arrays.asList(
            "黄州区",
            "团风县",
            "红安县",
            "麻城市",
            "罗田县",
            "英山县",
            "浠水县",
            "蕲春县",
            "武穴市",
            "黄梅县",
            "黄冈高新区",
            "市临空经济区",
            "龙感湖管理区",
            "白莲河示范区"
    );

    @Override
    public Answer<Object> countLightByStreet() {
        JSONObject resultJson = new JSONObject();

        // 1. 获取基础数据
        List<RectificationInfo> allTasks = fetchAllRectification(null);

        for (RectificationInfo info : allTasks) {
            String title = info.getTitle(); // 获取项目标题
            if (title != null) {
                switch (title) {
                    case "沿江高铁合武段":
                        info.setDepartmentName("麻城市"); // 替换责任部门
                        break;
                    case "鄂东(黄冈)数智物流综合服务中心":
                        info.setDepartmentName("黄冈国投集团");
                        break;
                    case "水质提升":
                        info.setDepartmentName("市生态环境局");
                        break;
                    case "黄冈城区供水管网漏损治理及老化供水设施更新改造项目":
                        info.setDepartmentName("市住新局");
                        break;
                    // 其他项目不做处理
                    default:
                        break;
                }
            }
        }

        List<RectificationV2Vo> streetDataList = getStreetData(allTasks);

        // 2. 定义区域列表（修正命名，明确业务含义）
        List<String> countyDistricts = Arrays.asList(
                "黄州区", "团风县", "红安县", "麻城市",
                "罗田县", "英山县", "浠水县", "蕲春县",
                "武穴市", "黄梅县"
        );
        List<String> functionalAreas = Arrays.asList(
                "龙感湖管理区", "白莲河示范区", "黄冈高新区", "市临空经济区"
        );

        // 3. 初始化统计变量（按区域分类命名，减少歧义）
        int countyRed = 0, countyGreen = 0, countyYellow = 0;
        int funcAreaRed = 0, funcAreaGreen = 0, funcAreaYellow = 0;

        // 4. 遍历数据并统计（使用迭代器安全操作集合）
        Iterator<RectificationV2Vo> iterator = streetDataList.iterator();
        while (iterator.hasNext()) {
            RectificationV2Vo streetData = iterator.next();
            String streetName = streetData.getStreet();

            // 累加县市区数据
            if (countyDistricts.contains(streetName)) {
                countyRed += streetData.getRed();
                countyGreen += streetData.getGreen();
                countyYellow += streetData.getYellow();
            }

            // 累加功能区数据
            if (functionalAreas.contains(streetName)) {
                funcAreaRed += streetData.getRed();
                funcAreaGreen += streetData.getGreen();
                funcAreaYellow += streetData.getYellow();
            }

            // 移除"市本级"数据（修正无效条件判断）
            if (CITY_LEVEL.equals(streetName)) {
                iterator.remove();
            }
        }

        streetDataList.sort((data1, data2) -> {
            String name1 = data1.getStreet();
            String name2 = data2.getStreet();
            // 获取两个区域在排序列表中的索引
            int index1 = AREA_ORDER.indexOf(name1);
            int index2 = AREA_ORDER.indexOf(name2);
            // 索引小的排前面；未在列表中的区域放最后（按名称自然排序）
            if (index1 == -1 && index2 == -1) {
                return name1.compareTo(name2); // 均不在列表中，按名称排序
            } else if (index1 == -1) {
                return 1; // 前者不在列表中，放后面
            } else if (index2 == -1) {
                return -1; // 后者不在列表中，放后面
            } else {
                return Integer.compare(index1, index2); // 按索引顺序排序
            }
        });

        // 5. 计算红灯率（封装工具方法，处理除零异常）
        int totalCounty = countyRed + countyGreen + countyYellow;
        BigDecimal countyRedRate = calculateRedRate(countyRed, totalCounty);

        int totalFuncArea = funcAreaRed + funcAreaGreen + funcAreaYellow;
        BigDecimal funcAreaRedRate = calculateRedRate(funcAreaRed, totalFuncArea);

        // 6. 构建统计描述（提取公共方法，避免重复拼接）
        String countyStats = buildStatsDescription(
                allTasks.size(), totalCounty, countyGreen, countyYellow, countyRed,
                countyRedRate, COUNTY_DISTRICT_COUNT + "个县市区"
        );
        String funcAreaStats = buildStatsDescription(
                allTasks.size(), totalFuncArea, funcAreaGreen, funcAreaYellow, funcAreaRed,
                funcAreaRedRate, FUNCTIONAL_AREA_COUNT + "个功能区"
        );

        // 7. 封装返回结果
        resultJson.put("streetList", streetDataList);
        resultJson.put("countyStats", countyStats);
        resultJson.put("functionalAreaStats", funcAreaStats);

        return Answer.ok(resultJson);
    }

    /**
     * 计算红灯率（处理总数为0的场景，避免除零异常）
     */
    private BigDecimal calculateRedRate(int redCount, int totalCount) {
        if (totalCount == 0) {
            return BigDecimal.ZERO;
        }
        return new BigDecimal(redCount)
                .divide(new BigDecimal(totalCount), 4, RoundingMode.HALF_UP)
                .multiply(new BigDecimal(100))
                .setScale(1, RoundingMode.HALF_UP);
    }

    /**
     * 构建统计描述字符串（复用逻辑，提升可维护性）
     */
    private String buildStatsDescription(int totalPlatform, int totalArea, int green, int yellow,
                                         int red, BigDecimal rate, String areaType) {
        return String.format(
                "平台共%d项任务，%s共%d项任务,绿灯数%d个,黄灯数%d个,红灯数%d个,红灯率占比%s%%",
                totalPlatform, areaType, totalArea, green, yellow, red, rate.toPlainString()
        );
    }

    @Override
    public Answer<Object> countLightByOrganization() {
        JSONObject resultJson = new JSONObject();
        // 获取所有整改信息列表
        List<RectificationInfo> rectificationInfoList = fetchAllRectification(null);
        // 查询市级整改数据
        Answer<Object> objectAnswer = this.queryRectificationV2NumCity();
        if (objectAnswer == null || objectAnswer.getResult() == null) {
            return Answer.error("查询整改数据失败");
        }

        int count = 0;
        int green = 0;
        int red = 0;
        int yellow = 0;
        List<RectificationV2Vo> result = (List<RectificationV2Vo>) objectAnswer.getResult();

        Map<String, List<RectificationInfo>> deptWarnTitles = new HashMap<>();
        for (RectificationInfo info : rectificationInfoList) {
            if (info.getWarnLevel() == 2) {
                String department = info.getDepartmentName();
                if (department == null) {
                    continue;
                }
                deptWarnTitles.computeIfAbsent(department, k -> new ArrayList<>())
                        .add(info);
            }
        }

        for (RectificationV2Vo vo : result) {
            String street = vo.getStreet();
            if (street == null) {
                continue;
            }

            // 累加统计数据
            count += vo.getTotal() == null ? 0 : vo.getTotal();
            green += vo.getGreen() == null ? 0 : vo.getGreen();
            red += vo.getRed() == null ? 0 : vo.getRed();
            yellow += vo.getYellow() == null ? 0 : vo.getYellow();

            List<RectificationInfo> titles = deptWarnTitles.get(street);
            if (titles != null && !titles.isEmpty()) {
                String remake = titles.stream()
                        .map(RectificationInfo::getTitle) // 提取每个元素的title
                        .collect(Collectors.joining("; ")); // 拼接字符串
                vo.setRedName(remake);

                String project = titles.stream()
                        .map(info -> {
                            List<String> parts = new ArrayList<>();
                            parts.add(info.getProjectType());
                            parts.add(info.getProjectChildType());
                            // 只添加非null的第三个字段
                            if (info.getProjectChildCategoryType() != null) {
                                parts.add(info.getProjectChildCategoryType());
                            }
                            return String.join("-", parts);
                        })
                        .collect(Collectors.joining("; "));
                vo.setProjectName(project);
            } else {
                vo.setProjectName("");
            }


        }

        List<RectificationV2Vo> sortedResult = result.stream()
                .sorted(Comparator.comparingDouble((RectificationV2Vo vo) -> { // 关键：显式类型
                    String rateStr = vo.getRate(); // 现在能正常解析 getRate()
                    if (rateStr == null || rateStr.isEmpty() || !rateStr.endsWith("%")) {
                        return -1.0; // 空值/非法值排最后
                    }
                    try {
                        return Double.parseDouble(rateStr.replace("%", ""));
                    } catch (NumberFormatException e) {
                        return -1.0;
                    }
                }).reversed()) // 降序（去掉则升序）
                .collect(Collectors.toList());

        BigDecimal redRate = BigDecimal.ZERO;
        if (count > 0) {
            redRate = new BigDecimal(red)
                    .divide(new BigDecimal(count), 4, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal(100))
                    .setScale(1, RoundingMode.HALF_UP);
        }

        String streetCount = String.format(
                "平台共%d项任务，市直单位共%d项任务，绿灯数%d个、黄灯数%d个、红灯数%d个，红灯率占比%s%%",
                rectificationInfoList.size(), count, green, yellow, red, redRate.toPlainString()
        );
        resultJson.put("streetCount", streetCount);
        resultJson.put("result", sortedResult);
        return Answer.ok(resultJson);
    }

    private static final String KEY_INDUSTRY_ENTERPRISE = "重点工业企业包保服务";
    private static final String KEY_SERVICE_ENTERPRISE = "重点服务业企业培育";
    private static final String PROCESS_INDUSTRY_GROWTH = "工业增长点不及预期";
    private static final String PROCESS_SERVICE_CULTIVATION = "服务业市场主体培育未完成目标";

    @Override
    public Answer<Object> countLight(JSONObject reqJson) {
        JSONObject reqJson1 = new JSONObject();
        reqJson1.put("pageSize", 9999);
        reqJson1.put("pageNum", 1);
        Integer pageSize = reqJson.getInteger("pageSize");
        Integer pageNum = reqJson.getInteger("pageNum");
        Answer<Object> objectAnswer = this.queryRectificationPageV2(reqJson1);
        Page<RectificationInfo> result = (Page<RectificationInfo>) objectAnswer.getResult();
        List<RectificationInfo> records = result.getRecords();
        // 把 records 里面三个项目的 责任部门改一下
        // 遍历集合，修改特定项目的责任部门
        for (RectificationInfo info : records) {
            String title = info.getTitle(); // 获取项目标题
            if (title != null) {
                switch (title) {
                    case "沿江高铁合武段":
                        info.setDepartmentName("麻城市"); // 替换责任部门
                        break;
                    case "鄂东(黄冈)数智物流综合服务中心":
                        info.setDepartmentName("黄冈国投集团");
                        break;
                    case "水质提升":
                        info.setDepartmentName("市生态环境局");
                        break;
                    case "黄冈城区供水管网漏损治理及老化供水设施更新改造项目":
                        info.setDepartmentName("市住新局");
                        break;
                    // 其他项目不做处理
                    default:
                        break;
                }
            }
        }

        Map<String, List<RectificationInfo>> map = new HashMap<>();
        if (records != null && !records.isEmpty()) {
            map = records.stream()
                    .filter(e -> StrUtil.isNotBlank(e.getDepartmentName()))
                    .collect(Collectors.groupingBy(RectificationInfo::getDepartmentName));
        }
        List<DeadlineNode> deadlineNodes = deadlineNodeMapper.selectList(new LambdaQueryWrapper<>());
        Map<Integer, List<DeadlineNode>> deadlineNodeMap = new HashMap<>();
        if (deadlineNodes != null && !deadlineNodes.isEmpty()) {
            deadlineNodeMap = deadlineNodes.stream().filter(e -> e.getRectificationId() != null).collect(Collectors.groupingBy(DeadlineNode::getRectificationId));
        }
        List<DeadlineRectificationPeriod> finalDeadlinePeriods =
                deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<DeadlineRectificationPeriod>()
                        .eq(DeadlineRectificationPeriod::getIsApproval, 1).orderByDesc(DeadlineRectificationPeriod::getId));
        Map<Integer, List<DeadlineRectificationPeriod>> deadlineRectificationPeriodMap = new HashMap<>();
        if (finalDeadlinePeriods != null) {
            deadlineRectificationPeriodMap = finalDeadlinePeriods.stream().filter(o -> o.getDeadlineNodeId() != null).collect(Collectors.groupingBy(DeadlineRectificationPeriod::getDeadlineNodeId));
        }
        Page<RectificationV2Vo> resultPage = new Page<>(pageNum, pageSize);
        List<RectificationV2Vo> rectificationV2VoList = new ArrayList<>();
        for (String departmentName : map.keySet()) {
            List<RectificationInfo> rectificationInfos = map.get(departmentName);
            List<RectificationInfo> rectificationInfoList = new ArrayList<>();
            int red = 0;
            if (rectificationInfos != null && !rectificationInfos.isEmpty())
                for (RectificationInfo rectificationInfo : rectificationInfos) {
                    if (2 == rectificationInfo.getWarnLevel()) {
                        red++;
                        List<DeadlineNode> deadlineNodeList = filiterNode(rectificationInfo, deadlineNodeMap.get(rectificationInfo.getId()));
                        List<DeadlineNode> deadlineList = new ArrayList<>();
                        if (deadlineNodeList != null && !deadlineNodeList.isEmpty()) {
                            for (DeadlineNode deadlineNode : deadlineNodeList) {
                                if (deadlineNode.getWarnLevel() != null)
                                    if (deadlineNode.getWarnLevel().equals("2")) {
                                        List<DeadlineRectificationPeriod> deadlineRectificationPeriodList = deadlineRectificationPeriodMap.get(deadlineNode.getId());
                                        if (deadlineRectificationPeriodList != null && !deadlineRectificationPeriodList.isEmpty()) {
                                            deadlineNode.setDeadlineRectificationPeriod(deadlineRectificationPeriodList.get(0).getRectificationTime());
                                        }
                                        if (deadlineNode.getDeadlineRectificationPeriod() == null) {
                                            deadlineNode.setDeadlineRectificationPeriod(deadlineNode.getDeadline());
                                        }
                                        if (deadlineNode.getDeadlineRectificationPeriod() == null) {
                                            continue;
                                        } else {
                                            Calendar calendar = Calendar.getInstance();
                                            calendar.setTime(deadlineNode.getDeadlineRectificationPeriod());
                                            calendar.set(Calendar.HOUR_OF_DAY, 23);
                                            calendar.set(Calendar.MINUTE, 59);
                                            calendar.set(Calendar.SECOND, 59);
                                            deadlineNode.setDeadlineRectificationPeriod(calendar.getTime());
                                        }
                                        Date endTime = deadlineNode.getEndTime();
                                        Date today = new Date();
                                        Date rectificationPeriod = deadlineNode.getDeadlineRectificationPeriod();
                                        if (endTime == null && rectificationPeriod.before(today)) {
                                            long diffInMillies = today.getTime() - rectificationPeriod.getTime();
                                            if (diffInMillies < 0) {
                                                diffInMillies = 0;
                                            } else {
                                                diffInMillies = diffInMillies / 1000 / 60 / 60 / 24;
                                            }
                                            deadlineNode.setOverdueTime(String.valueOf(diffInMillies));
                                        } else {
                                            if (endTime != null && endTime.after(rectificationPeriod)) {
                                                long diffInMillies = endTime.getTime() - rectificationPeriod.getTime();
                                                if (diffInMillies < 0) {
                                                    diffInMillies = 0;
                                                } else {
                                                    diffInMillies = diffInMillies / 1000 / 60 / 60 / 24;
                                                }
                                                deadlineNode.setOverdueTime(String.valueOf(diffInMillies));
                                            }
                                        }
                                        String nodeName = Objects.nonNull(deadlineNode.getName()) ? deadlineNode.getName() : "";
                                        String nodeContent = Objects.nonNull(deadlineNode.getContent()) ? deadlineNode.getContent() : "";
                                        // 2. 若名称包含“节点”，则拼接“名称+内容”（避免空指针）
                                        if (nodeName.contains("节点")) {
                                            deadlineNode.setName(nodeName + " " + nodeContent);
                                        }

                                        String projectChildCategoryType = rectificationInfo.getProjectChildCategoryType();
                                        String projectChildType = rectificationInfo.getProjectChildType();
                                        if (StringUtils.isBlank(projectChildCategoryType)) {
                                            projectChildCategoryType = projectChildType;
                                            rectificationInfo.setProjectChildCategoryType(projectChildCategoryType);
                                        }
                                        if (projectChildCategoryType.contains(KEY_INDUSTRY_ENTERPRISE)) {
                                            deadlineNode.setProcess(PROCESS_INDUSTRY_GROWTH);
                                            deadlineNode.setRemark(PROCESS_INDUSTRY_GROWTH);
                                        } else if (projectChildCategoryType.contains(KEY_SERVICE_ENTERPRISE)) {
                                            deadlineNode.setProcess(PROCESS_SERVICE_CULTIVATION);
                                            deadlineNode.setRemark(PROCESS_SERVICE_CULTIVATION);
                                        }
                                        deadlineList.add(deadlineNode);
                                    }
                            }
                        }else {
                            String projectChildCategoryType = rectificationInfo.getProjectChildCategoryType();
                            String projectChildType = rectificationInfo.getProjectChildType();
                            if (StringUtils.isBlank(projectChildCategoryType)) {
                                projectChildCategoryType = projectChildType;
                                rectificationInfo.setProjectChildCategoryType(projectChildCategoryType);
                            }
                            DeadlineNode deadlineNode = new DeadlineNode();
                            if (projectChildCategoryType.contains(KEY_INDUSTRY_ENTERPRISE)) {
                                deadlineNode.setProcess(PROCESS_INDUSTRY_GROWTH);
                                deadlineNode.setRemark(PROCESS_INDUSTRY_GROWTH);
                            } else if (projectChildCategoryType.contains(KEY_SERVICE_ENTERPRISE)) {
                                deadlineNode.setProcess(PROCESS_SERVICE_CULTIVATION);
                                deadlineNode.setRemark(PROCESS_SERVICE_CULTIVATION);
                            }
                            deadlineList.add(deadlineNode);
                        }
                        rectificationInfo.setDeadlineNodeList(deadlineList);
                        rectificationInfoList.add(rectificationInfo);
                    }
                }
            RectificationV2Vo rectificationV2Vo = new RectificationV2Vo();
            rectificationV2Vo.setStreet(departmentName);
            rectificationV2Vo.setRed(red);
            rectificationV2Vo.setTotal(rectificationInfos.size());
            BigDecimal redB = new BigDecimal(red);
            BigDecimal totalB = new BigDecimal(rectificationInfos.size());
            BigDecimal redRate = redB.divide(totalB, 4, RoundingMode.HALF_UP) // 先保留4位小数（如 0.3050）
                    .multiply(new BigDecimal(100)) // 转为百分比（如 30.50）
                    .setScale(1, RoundingMode.HALF_UP); // 最终保留2位小数
            rectificationV2Vo.setRate(redRate + "%");
            rectificationV2Vo.setRectificationInfos(rectificationInfoList);
            rectificationV2VoList.add(rectificationV2Vo);
        }
        rectificationV2VoList = rectificationV2VoList.stream()
                .filter(vo -> vo != null && vo.getRed() != 0) // 只保留red≠0的元素
                .collect(Collectors.toList());

        List<RectificationV2Vo> sortedResult = rectificationV2VoList.stream()
                .sorted(Comparator.comparingDouble((RectificationV2Vo vo) -> { // 关键：显式类型
                    String rateStr = vo.getRate(); // 现在能正常解析 getRate()
                    if (rateStr == null || rateStr.isEmpty() || !rateStr.endsWith("%")) {
                        return -1.0; // 空值/非法值排最后
                    }
                    try {
                        return Double.parseDouble(rateStr.replace("%", ""));
                    } catch (NumberFormatException e) {
                        return -1.0;
                    }
                }).reversed()) // 降序（去掉则升序）
                .collect(Collectors.toList());

        //手动分页

        int total = sortedResult.size();
        int pages = (total + pageSize - 1) / pageSize;

        if (pageNum > pages) {
            pageNum = pages;
        }
        if (pageNum < 1) {
            pageNum = 1;
        }

        int fromIndex = (pageNum - 1) * pageSize;
        int toIndex = Math.min(fromIndex + pageSize, total);

        // 使用子列表创建分页结果
        List<RectificationV2Vo> pageRecords = sortedResult.subList(fromIndex, toIndex);

        resultPage.setRecords(pageRecords);
        resultPage.setTotal(total);
        resultPage.setPages(pages);
        resultPage.setCurrent(pageNum);
        resultPage.setSize(pageSize);

        return Answer.ok(resultPage);
    }

    @Override
    public Answer<Object> lightRectification(JSONObject reqJson) {
        JSONObject reqJson1 = new JSONObject();
        reqJson1.put("pageSize", 9999);
        reqJson1.put("pageNum", 1);
        Answer<Object> objectAnswer = this.queryRectificationPageV2(reqJson1);
        Page<RectificationInfo> result = (Page<RectificationInfo>) objectAnswer.getResult();
        List<RectificationInfo> records = result.getRecords();
        Map<String, List<RectificationInfo>> map = new HashMap<>();
        if (records != null && !records.isEmpty()) {
            map = records.stream()
                    .filter(e -> StrUtil.isNotBlank(e.getDepartmentName()))
                    .collect(Collectors.groupingBy(RectificationInfo::getDepartmentName));
        }
        List<DeadlineNode> deadlineNodes = deadlineNodeMapper.selectList(new LambdaQueryWrapper<>());
        Map<Integer, List<DeadlineNode>> deadlineNodeMap = new HashMap<>();
        if (deadlineNodes != null && !deadlineNodes.isEmpty()) {
            deadlineNodeMap = deadlineNodes.stream().filter(e -> e.getRectificationId() != null).collect(Collectors.groupingBy(DeadlineNode::getRectificationId));
        }
        List<DeadlineRectificationPeriod> finalDeadlinePeriods =
                deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<DeadlineRectificationPeriod>()
                        .eq(DeadlineRectificationPeriod::getIsApproval, 1).orderByDesc(DeadlineRectificationPeriod::getId));
        Map<Integer, List<DeadlineRectificationPeriod>> deadlineRectificationPeriodMap = new HashMap<>();
        if (finalDeadlinePeriods != null) {
            deadlineRectificationPeriodMap = finalDeadlinePeriods.stream().filter(o -> o.getDeadlineNodeId() != null).collect(Collectors.groupingBy(DeadlineRectificationPeriod::getDeadlineNodeId));
        }
        Integer pageNum = reqJson.getInteger("pageNum");
        Integer pageSize = reqJson.getInteger("pageSize");
        Page<RectificationInfo> resultPage = new Page<>(pageNum, pageSize);
        List<RectificationInfo> rectificationInfoList = new ArrayList<>();
        for (String departmentName : map.keySet()) {
            List<RectificationInfo> rectificationInfos = map.get(departmentName);
            if (rectificationInfos != null && !rectificationInfos.isEmpty())
                for (RectificationInfo rectificationInfo : rectificationInfos) {
                    List<DeadlineNode> deadlineList = new ArrayList<>();
                    if (2 == rectificationInfo.getWarnLevel()) {
                        List<DeadlineNode> deadlineNodeList = filiterNode(rectificationInfo, deadlineNodeMap.get(rectificationInfo.getId()));
                        if (deadlineNodeList != null && !deadlineNodeList.isEmpty())
                            for (DeadlineNode deadlineNode : deadlineNodeList) {
                                if (deadlineNode.getWarnLevel().equals("2")) {
                                    List<DeadlineRectificationPeriod> deadlineRectificationPeriodList = deadlineRectificationPeriodMap.get(deadlineNode.getId());
                                    if (deadlineRectificationPeriodList != null && !deadlineRectificationPeriodList.isEmpty()) {
                                        deadlineNode.setDeadlineRectificationPeriod(deadlineRectificationPeriodList.get(0).getRectificationTime());
                                        deadlineNode.setUpdateReason(deadlineRectificationPeriodList.get(0).getRequestRemark());
                                    }
                                    deadlineList.add(deadlineNode);
                                }
                            }
                        if (deadlineList != null && !deadlineList.isEmpty()) {
                            rectificationInfo.setDeadlineNodeList(deadlineList);
                            rectificationInfoList.add(rectificationInfo);
                        }
                    }
                }
        }
        int total = rectificationInfoList.size();
        int pages = (total + pageSize - 1) / pageSize;

        if (pageNum > pages) {
            pageNum = pages;
        }
        if (pageNum < 1) {
            pageNum = 1;
        }

        int fromIndex = (pageNum - 1) * pageSize;
        int toIndex = Math.min(fromIndex + pageSize, total);

        // 使用子列表创建分页结果
        List<RectificationInfo> pageRecords = rectificationInfoList.subList(fromIndex, toIndex);

        resultPage.setRecords(pageRecords);
        resultPage.setTotal(total);
        resultPage.setPages(pages);
        resultPage.setCurrent(pageNum);
        resultPage.setSize(pageSize);

        return Answer.ok(resultPage);
    }

    @Override
    public void exportCountLightByStreet(DeadlineNodeOutput req, HttpServletResponse response) {
        // 1. 调用统计接口并校验结果
        Answer<Object> objectAnswer = this.countLightByStreet();
        if (objectAnswer == null) {
            throw new RuntimeException("查询接口返回空，请检查接口实现");
        }
        if (objectAnswer.getCode() != 200) {
            throw new RuntimeException(String.format("分页查询失败：状态码=%d，提示=%s",
                    objectAnswer.getCode(), objectAnswer.getMsg()));
        }

        // 2. 解析返回结果（处理空值与格式校验）
        Object resultObj = objectAnswer.getResult();
        if (!(resultObj instanceof JSONObject)) {
            throw new RuntimeException("接口返回结果格式错误，非JSONObject类型");
        }
        JSONObject result = (JSONObject) resultObj;

        JSONArray streetJsonArray = result.getJSONArray("streetList");
        List<RectificationV2Vo> streetList = (streetJsonArray == null)
                ? new ArrayList<>()
                : streetJsonArray.toJavaList(RectificationV2Vo.class);

        String countyStats = result.getString("countyStats");
        String functionalAreaStats = result.getString("functionalAreaStats");
        countyStats = (countyStats == null) ? "" : countyStats;
        functionalAreaStats = (functionalAreaStats == null) ? "" : functionalAreaStats;

        // 3. 构建Excel数据行（确保与表头key匹配，同时记录区域名称用于定位）
        List<Map<String, Object>> dataRows = streetList.stream().map(streetVo -> {
            Map<String, Object> rowMap = new LinkedHashMap<>();
            String streetName = streetVo.getStreet() == null ? "" : streetVo.getStreet();
            rowMap.put("单位", streetName); // 区域名称（用于定位“市临空经济区”）
            rowMap.put("任务数", streetVo.getTotal() == null ? 0 : streetVo.getTotal());
            rowMap.put("绿灯数", streetVo.getGreen() == null ? 0 : streetVo.getGreen());
            rowMap.put("黄灯数", streetVo.getYellow() == null ? 0 : streetVo.getYellow());
            rowMap.put("红灯数", streetVo.getRed() == null ? 0 : streetVo.getRed());
            rowMap.put("红灯率", streetVo.getTotalGreenRate() == null ? "0.0%" : streetVo.getTotalGreenRate());
            rowMap.put("红灯个数(828书记座谈会表态项目)", streetVo.getRed828() == null ? 0 : streetVo.getRed828());
            rowMap.put("占红灯数量比重(828书记座谈会表态项目)", streetVo.getWeight828() == null ? "0.0%" : streetVo.getWeight828());
            return rowMap;
        }).collect(Collectors.toList());

        // 4. 初始化Excel并配置响应
        String fileName = "项目列表导出_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + ".xlsx";
        try (Workbook wb = new XSSFWorkbook();
             OutputStream out = response.getOutputStream()) {

            Sheet sheet = wb.createSheet("统计数据");
            // 定义表头（固定8列，与数据行key对应）
            LinkedHashMap<String, String> headerMap = new LinkedHashMap<>();
            headerMap.put("单位", "单位");
            headerMap.put("任务数", "任务数");
            headerMap.put("绿灯数", "绿灯数");
            headerMap.put("黄灯数", "黄灯数");
            headerMap.put("红灯数", "红灯数");
            headerMap.put("红灯率", "红灯率");
            headerMap.put("红灯个数(828书记座谈会表态项目)", "红灯个数(828书记座谈会表态项目)");
            headerMap.put("占红灯数量比重(828书记座谈会表态项目)", "占红灯数量比重(828书记座谈会表态项目)");
            List<String> headerKeys = new ArrayList<>(headerMap.keySet());
            int columnCount = headerKeys.size();
            final String TARGET_AREA = "市临空经济区"; // 目标定位区域

            // 5. 定义Excel样式（统计行用专用样式突出）
            CellStyle titleStyle = createTitleStyle(wb);
            CellStyle headerStyle = createHeaderStyle(wb);
            CellStyle statsStyle = createStatsStyle(wb); // 统计行样式（浅灰背景+加粗）
            CellStyle normalStyle = createNormalStyle(wb);
            CellStyle wrapStyle = createWrapStyle(wb);

            // 6. 写入Excel内容（核心：定位市临空经济区并插入统计行）
            int currentRowNum = 0;

            // ---------------------- 6.1 第1行：标题行 ----------------------
            Row titleRow = sheet.createRow(currentRowNum++);
            Cell titleCell = titleRow.createCell(0);
            titleCell.setCellValue("县市区、功能区总体情况统计");
            titleCell.setCellStyle(titleStyle);
            sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, columnCount - 1));
            titleRow.setHeightInPoints(22);

            // ---------------------- 6.2 第2行：表头行 ----------------------
            Row headerRow = sheet.createRow(currentRowNum++);
            for (int col = 0; col < columnCount; col++) {
                Cell cell = headerRow.createCell(col);
                cell.setCellValue(headerMap.get(headerKeys.get(col)));
                cell.setCellStyle(headerStyle);
            }
            headerRow.setHeightInPoints(20);

            // ---------------------- 6.3 第3行：县市区统计行 ----------------------
            Row countyStatsRow = sheet.createRow(currentRowNum++);
            Cell countyStatsCell = countyStatsRow.createCell(0);
            countyStatsCell.setCellValue(countyStats);
            countyStatsCell.setCellStyle(statsStyle);
            sheet.addMergedRegion(new CellRangeAddress(2, 2, 0, columnCount - 1));
            countyStatsRow.setHeightInPoints(18);

            // ---------------------- 6.4 主体数据行：定位市临空经济区并插入统计行 ----------------------
            for (Map<String, Object> rowData : dataRows) {
                String currentArea = (String) rowData.get("单位"); // 当前行的区域名称

                // 关键逻辑：若当前行是“市临空经济区”，先插入functionalAreaStats再写数据
                if (TARGET_AREA.equals(currentArea)) {
                    Row funcStatsRow = sheet.createRow(currentRowNum++);
                    Cell funcStatsCell = funcStatsRow.createCell(0);
                    funcStatsCell.setCellValue(functionalAreaStats);
                    funcStatsCell.setCellStyle(statsStyle);
                    // 合并所有列，突出统计信息
                    sheet.addMergedRegion(new CellRangeAddress(funcStatsRow.getRowNum(), funcStatsRow.getRowNum(), 0, columnCount - 1));
                    funcStatsRow.setHeightInPoints(18);
                }

                // 写入当前区域的数据行
                Row dataRow = sheet.createRow(currentRowNum++);
                for (int col = 0; col < columnCount; col++) {
                    String key = headerKeys.get(col);
                    Object value = rowData.get(key);
                    Cell cell = dataRow.createCell(col);

                    // 处理不同数据类型，避免格式错乱
                    if (value instanceof Integer) {
                        cell.setCellValue((Integer) value);
                    } else if (value instanceof Double) {
                        cell.setCellValue((Double) value);
                    } else {
                        cell.setCellValue(value != null ? value.toString() : "");
                    }

                    // 任务数列用换行样式，其他用普通样式
                    cell.setCellStyle("任务数".equals(key) ? wrapStyle : normalStyle);
                }
                dataRow.setHeightInPoints(18);
            }

            // 7. 调整Excel格式（列宽+冻结窗格）
            for (int col = 0; col < columnCount; col++) {
                int headerLength = headerMap.get(headerKeys.get(col)).length();
                int columnWidth; // 先声明变量，在switch中赋值
                // 列宽适配：“单位”列留足20字符，“占红灯比重”列留足18字符，其他列12字符
                switch (col) {
                    case 0:
                        // 单位列（区域名称可能较长，如“市临空经济区”）
                        columnWidth = 20 * 256;
                        break;
                    case 7:
                        // 占红灯数量比重列（文本较长，如“100.00%”）
                        columnWidth = 18 * 256;
                        break;
                    default:
                        // 其他列：按表头长度的2倍或最小12字符适配，避免文本截断
                        columnWidth = Math.max(headerLength * 2, 12) * 256;
                        break;
                }
                sheet.setColumnWidth(col, columnWidth);
            }
            // 冻结前3行（标题+表头+县市区统计），滚动时表头不消失
            sheet.createFreezePane(0, 3);

            // 8. 响应配置（避免中文乱码+禁止缓存）
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()));
            response.setHeader("Pragma", "no-cache");
            response.setHeader("Cache-Control", "no-store");

            // 9. 写出Excel到响应流
            wb.write(out);
            out.flush();

        } catch (IOException e) {
            throw new RuntimeException("Excel导出失败：IO异常", e);
        } catch (Exception e) {
            throw new RuntimeException("Excel导出失败：" + e.getMessage(), e);
        }
    }

// ---------------------- 样式工具方法（复用性优化） ----------------------

    /**
     * 标题样式：居中、加粗14号字体
     */
    private CellStyle createTitleStyle(Workbook wb) {
        CellStyle style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        Font font = wb.createFont();
        font.setBold(true);
        font.setFontHeightInPoints((short) 14);
        style.setFont(font);
        return style;
    }

    /**
     * 表头样式：居中、加粗11号字体、全边框
     */
    private CellStyle createHeaderStyle(Workbook wb) {
        CellStyle style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setBorderTop(BorderStyle.THIN);
        style.setBorderBottom(BorderStyle.THIN);
        style.setBorderLeft(BorderStyle.THIN);
        style.setBorderRight(BorderStyle.THIN);
        Font font = wb.createFont();
        font.setBold(true);
        font.setFontHeightInPoints((short) 11);
        style.setFont(font);
        return style;
    }

    /**
     * 统计行样式：左对齐、加粗11号字体、浅灰背景（突出统计信息）
     */
    private CellStyle createStatsStyle(Workbook wb) {
        CellStyle style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.LEFT);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        Font font = wb.createFont();
        font.setBold(true);
        font.setFontHeightInPoints((short) 11);
        style.setFont(font);
        return style;
    }

    /**
     * 普通数据样式：左对齐、11号字体、全边框
     */
    private CellStyle createNormalStyle(Workbook wb) {
        CellStyle style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.LEFT);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setBorderTop(BorderStyle.THIN);
        style.setBorderBottom(BorderStyle.THIN);
        style.setBorderLeft(BorderStyle.THIN);
        style.setBorderRight(BorderStyle.THIN);
        Font font = wb.createFont();
        font.setFontHeightInPoints((short) 11);
        style.setFont(font);
        return style;
    }

    /**
     * 换行样式：自动换行（适配任务数列长文本）
     */
    private CellStyle createWrapStyle(Workbook wb) {
        CellStyle style = wb.createCellStyle();
        style.setWrapText(true);
        style.setAlignment(HorizontalAlignment.LEFT);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setBorderTop(BorderStyle.THIN);
        style.setBorderBottom(BorderStyle.THIN);
        style.setBorderLeft(BorderStyle.THIN);
        style.setBorderRight(BorderStyle.THIN);
        Font font = wb.createFont();
        font.setFontHeightInPoints((short) 11);
        style.setFont(font);
        return style;
    }

    @Value("${excel.template.count-light:template/countLightByOrganization.xlsx}")
    private String countLightByOrganizationTemplatePath;

    @Override
    public void exportCountLightByOrganization(DeadlineNodeOutput req, HttpServletResponse response) {

        Answer<Object> objectAnswer = this.countLightByOrganization();
        if (objectAnswer == null) {
            throw new RuntimeException("分页查询接口返回空，请检查接口实现");
        }
        if (objectAnswer.getCode() != 200) {
            throw new RuntimeException(String.format("分页查询失败：状态码=%d，提示=%s",
                    objectAnswer.getCode(), objectAnswer.getMsg()));
        }

        // 2. 解析返回结果（仅调整streetCount处理逻辑）
        JSONObject result = (JSONObject) objectAnswer.getResult();
        Assert.notNull(result, "统计接口返回结果格式异常，result为null");

        JSONArray dataArray = result.getJSONArray("result");
        Assert.notNull(dataArray, "统计接口返回结果中，数据列表为null");
        List<RectificationV2Vo> streetList = dataArray.toJavaList(RectificationV2Vo.class);

        // 处理streetCount：字符串格式统计文字，仅处理空值（不做数字校验）
        String streetCount = result.getString("streetCount");
        // 空值默认显示“暂无统计信息”，非空则直接使用原字符串（保留统计文字）
        streetCount = (streetCount == null || "".equals(streetCount.trim())) ? "暂无统计信息" : streetCount;

        // 3. 初始化响应（不变）
        String fileName = "市直单位总体情况统计表_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + ".xlsx";
        EasyExcelUtil.initResponse(response, fileName);

        // 4. try-with-resources 自动关闭资源（不变）
        try (
                OutputStream out = response.getOutputStream();
                InputStream templateFile = new ClassPathResource(countLightByOrganizationTemplatePath).getInputStream();
                com.alibaba.excel.ExcelWriter writer = EasyExcel.write(out)
                        .withTemplate(templateFile)
                        .excelType(com.alibaba.excel.support.ExcelTypeEnum.XLSX)
                        .build()
        ) {
            // 配置工作表（不变）
            WriteSheet sheet = EasyExcel.writerSheet(0)
                    .sheetName("市直单位统计")
                    .build();

            // 填充单个统计值（直接使用字符串统计文字，不额外拼接）
            JSONObject countData = new JSONObject();
            countData.put("streetCount", streetCount); // 直接放原统计文字，比如“共15个市直单位”
            writer.fill(countData, sheet);

            // 配置列表填充规则（不变）
            FillConfig fillConfig = FillConfig.builder()
                    .forceNewRow(true) // 避免覆盖模板
                    .build();

            // 填充列表数据（不变）
            if (streetList.isEmpty()) {
                RectificationV2Vo emptyVo = new RectificationV2Vo();
                emptyVo.setStreet("无统计数据");
                emptyVo.setTotal(0);
                emptyVo.setGreen(0);
                emptyVo.setYellow(0);
                emptyVo.setRed(0);
                emptyVo.setProjectName("");
                emptyVo.setRate("0.0%");
                emptyVo.setRemake("");
                streetList.add(emptyVo);
            }
            writer.fill(streetList, fillConfig, sheet);

        } catch (IOException e) {
            throw new RuntimeException("导出平台项目统计表失败：" + e.getMessage(), e);
        } catch (Exception e) {
            throw new RuntimeException("导出异常：" + e.getMessage(), e);
        }
    }

    /**
     * 红灯项目情况统计表
     * @param req
     * @param response
     */
    @Override
    public void exportCountLight(DeadlineNodeOutput req, HttpServletResponse response) {
        JSONObject reqJson = new JSONObject();
        reqJson.put("pageSize", 9999);
        reqJson.put("pageNum", 1);
        Answer<Object> objectAnswer = this.countLight(reqJson);

        if (objectAnswer == null) {
            throw new RuntimeException("查询接口返回空，请检查接口实现");
        }
        if (objectAnswer.getCode() != 200) {
            throw new RuntimeException(String.format("分页查询失败：状态码=%d，提示=%s",
                    objectAnswer.getCode(), objectAnswer.getMsg()));
        }

        Page<RectificationV2Vo> rectificationV2VoPage = (Page<RectificationV2Vo>) objectAnswer.getResult();
        List<RectificationV2Vo> lightList = rectificationV2VoPage.getRecords();

        // 生成最终数据行和合并信息
        ExportDataResult exportData = generateExportData(lightList);

        String fileName = "项目列表导出" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + ".xlsx";
        ExcelWriter writer = ExcelUtil.getBigWriter();
        try {
            LinkedHashMap<String, String> header = new LinkedHashMap<>();
            header.put("序号", "序号");
            header.put("单位", "单位");
            header.put("项目数", "项目数");
            header.put("红灯数", "红灯数");
            header.put("红灯率", "红灯率");
            header.put("红灯项目", "红灯项目");
            header.put("牵头领导", "牵头领导");
            header.put("责任人", "责任人");
            header.put("备注（项目类型）", "备注（项目类型）");
            header.put("滞后环节", "滞后环节");
            header.put("超期时间", "超期时间");
            header.put("滞后原因", "滞后原因");

            header.forEach(writer::addHeaderAlias);
            writer.setOnlyAlias(true);

            String title = "红灯项目情况统计";
            writer.merge(header.size() - 1, title);

            Workbook wb = writer.getWorkbook();
            Sheet sheet = writer.getSheet();
            Row r0 = sheet.getRow(0) != null ? sheet.getRow(0) : sheet.createRow(0);
            Cell c0 = r0.getCell(0) != null ? r0.getCell(0) : r0.createCell(0);
            CellStyle titleStyle = wb.createCellStyle();
            titleStyle.setAlignment(HorizontalAlignment.CENTER);
            titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            Font titleFont = wb.createFont();
            titleFont.setBold(true);
            titleFont.setFontHeightInPoints((short) 12);
            titleStyle.setFont(titleFont);
            c0.setCellStyle(titleStyle);
            r0.setHeightInPoints(22);

            sheet.createFreezePane(0, 2); // 冻结标题行+表头行

            // 创建自动换行的单元格样式
            CellStyle wrapStyle = wb.createCellStyle();
            wrapStyle.setWrapText(true);

            // 写入数据（数据从第2行开始：0=标题，1=表头）
            writer.write(exportData.getRows(), true);

            // 设置列宽
            int i = 0;
            for (String ignored : header.keySet()) {
                writer.setColumnWidth(i, 20);
                if (i == 1) { // 第2列（索引1）应用自动换行
                    sheet.setDefaultColumnStyle(i, wrapStyle);
                }
                i++;
            }

            // 应用合并信息
            applyMergeRegions(sheet, exportData.getMergeInfo(), 2);

            // 输出Excel文件
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + URLEncoder.encode(fileName, java.nio.charset.StandardCharsets.UTF_8.toString()));
            ServletOutputStream out = response.getOutputStream();
            writer.flush(out, true);
            out.flush();
        } catch (Exception e) {
            throw new RuntimeException("导出失败", e);
        } finally {
            writer.close(); // 关闭流，释放资源
        }
    }

    /**
     * 生成导出数据和合并信息（按照前端逻辑）
     */
    private ExportDataResult generateExportData(List<RectificationV2Vo> lightList) {
        List<Map<String, Object>> rows = new ArrayList<>();
        MergeInfo mergeInfo = new MergeInfo();

        int dataIndex = 0; // 行索引（用于合并定位）
        int projectIndex = 0; // 项目级序号（按单位/项目递增）

        for (RectificationV2Vo streetItem : lightList) {
            String street = streetItem.getStreet();
            Integer total = streetItem.getTotal();
            Integer red = streetItem.getRed();
            String rate = streetItem.getRate();
            List<RectificationInfo> rectificationInfos = streetItem.getRectificationInfos();

            projectIndex++; // 每个单位/项目对应一个序号

            if (rectificationInfos != null && !rectificationInfos.isEmpty()) {
                // 计算单位的总行数（所有项目的滞后环节之和）
                int streetRowCount = rectificationInfos.stream()
                        .mapToInt(rectification -> Math.max(rectification.getDeadlineNodeList() != null ?
                                rectification.getDeadlineNodeList().size() : 1, 1))
                        .sum();

                // 记录单位级列的合并信息（包含序号列）
                mergeInfo.addIndexMerge(dataIndex, streetRowCount); // 序号列按单位合并
                mergeInfo.addStreetMerge(dataIndex, streetRowCount);

                for (RectificationInfo rectification : rectificationInfos) {
                    String title = rectification.getTitle();
                    String leadLeader = rectification.getLeadLeader();
                    String personLiaible = rectification.getPersonLiaible();
                    String projectChildCategoryType = rectification.getProjectChildCategoryType();
                    List<DeadlineNode> deadlineNodeList = rectification.getDeadlineNodeList();

                    int projectRowCount = Math.max(deadlineNodeList != null ? deadlineNodeList.size() : 1, 1);
                    int projectStartIndex = dataIndex;

                    // 记录项目级列的合并信息
                    mergeInfo.addProjectMerge(projectStartIndex, projectRowCount);

                    if (deadlineNodeList != null && !deadlineNodeList.isEmpty()) {
                        // 有滞后环节：多行共用同一项目信息
                        for (int nodeIndex = 0; nodeIndex < deadlineNodeList.size(); nodeIndex++) {
                            DeadlineNode node = deadlineNodeList.get(nodeIndex);
                            Map<String, Object> row = new LinkedHashMap<>();

                            // 序号列：只在第一行显示，整个单位共用同一个序号
                            row.put("序号", nodeIndex == 0 ? projectIndex : "");

                            // 单位相关列：只在第一行显示
                            row.put("单位", nodeIndex == 0 ? street : "");
                            row.put("项目数", nodeIndex == 0 ? total : "");
                            row.put("红灯数", nodeIndex == 0 ? red : "");
                            row.put("红灯率", nodeIndex == 0 ? rate : "");

                            // 项目相关列：只在第一行显示
                            row.put("红灯项目", nodeIndex == 0 ? title : "");
                            row.put("牵头领导", nodeIndex == 0 ? leadLeader : "");
                            row.put("责任人", nodeIndex == 0 ? personLiaible : "");
                            row.put("备注（项目类型）", nodeIndex == 0 ? projectChildCategoryType : "");

                            // 滞后环节相关列：每行都显示
                            row.put("滞后环节", node.getName());
                            row.put("超期时间", node.getOverdueTime() != null ? node.getOverdueTime() + "天" : "");
                            row.put("滞后原因", node.getRemark());

                            rows.add(row);
                            dataIndex++;
                        }
                    } else {
                        // 无滞后环节：单行显示所有信息
                        Map<String, Object> row = new LinkedHashMap<>();
                        row.put("序号", projectIndex);
                        row.put("单位", street);
                        row.put("项目数", total);
                        row.put("红灯数", red);
                        row.put("红灯率", rate);
                        row.put("红灯项目", title);
                        row.put("牵头领导", leadLeader);
                        row.put("责任人", personLiaible);
                        row.put("备注（项目类型）", projectChildCategoryType);
                        row.put("滞后环节", "");
                        row.put("超期时间", "");
                        row.put("滞后原因", "");

                        rows.add(row);
                        dataIndex++;
                    }
                }
            } else {
                // 无项目：单行显示单位信息
                Map<String, Object> row = new LinkedHashMap<>();
                row.put("序号", projectIndex);
                row.put("单位", street);
                row.put("项目数", total);
                row.put("红灯数", red);
                row.put("红灯率", rate);
                row.put("红灯项目", "");
                row.put("牵头领导", "");
                row.put("责任人", "");
                row.put("备注（项目类型）", "");
                row.put("滞后环节", "");
                row.put("超期时间", "");
                row.put("滞后原因", "");

                rows.add(row);

                // 记录合并信息（所有列都合并1行）
                mergeInfo.addIndexMerge(dataIndex, 1);
                mergeInfo.addStreetMerge(dataIndex, 1);
                mergeInfo.addProjectMerge(dataIndex, 1);
                dataIndex++;
            }
        }

        return new ExportDataResult(rows, mergeInfo);
    }

    /**
     * 应用合并区域到Excel表格
     */
    private void applyMergeRegions(Sheet sheet, MergeInfo mergeInfo, int startRow) {
        // 序号列合并
        for (MergeRange range : mergeInfo.getIndexMerges()) {
            if (range.getRowspan() > 1) {
                sheet.addMergedRegion(new CellRangeAddress(
                        startRow + range.getIndex(),
                        startRow + range.getIndex() + range.getRowspan() - 1,
                        0, 0)); // 序号列（索引0）
            }
        }

        // 单位相关列合并：单位、项目数、红灯数、红灯率
        for (MergeRange range : mergeInfo.getStreetMerges()) {
            if (range.getRowspan() > 1) {
                // 单位列（索引1）
                sheet.addMergedRegion(new CellRangeAddress(
                        startRow + range.getIndex(),
                        startRow + range.getIndex() + range.getRowspan() - 1,
                        1, 1));
                // 项目数列（索引2）
                sheet.addMergedRegion(new CellRangeAddress(
                        startRow + range.getIndex(),
                        startRow + range.getIndex() + range.getRowspan() - 1,
                        2, 2));
                // 红灯数列（索引3）
                sheet.addMergedRegion(new CellRangeAddress(
                        startRow + range.getIndex(),
                        startRow + range.getIndex() + range.getRowspan() - 1,
                        3, 3));
                // 红灯率列（索引4）
                sheet.addMergedRegion(new CellRangeAddress(
                        startRow + range.getIndex(),
                        startRow + range.getIndex() + range.getRowspan() - 1,
                        4, 4));
            }
        }

        // 项目相关列合并：红灯项目、牵头领导、责任人、备注（项目类型）
        for (MergeRange range : mergeInfo.getProjectMerges()) {
            if (range.getRowspan() > 1) {
                // 红灯项目列（索引5）
                sheet.addMergedRegion(new CellRangeAddress(
                        startRow + range.getIndex(),
                        startRow + range.getIndex() + range.getRowspan() - 1,
                        5, 5));
                // 牵头领导列（索引6）
                sheet.addMergedRegion(new CellRangeAddress(
                        startRow + range.getIndex(),
                        startRow + range.getIndex() + range.getRowspan() - 1,
                        6, 6));
                // 责任人列（索引7）
                sheet.addMergedRegion(new CellRangeAddress(
                        startRow + range.getIndex(),
                        startRow + range.getIndex() + range.getRowspan() - 1,
                        7, 7));
                // 备注（项目类型）列（索引8）
                sheet.addMergedRegion(new CellRangeAddress(
                        startRow + range.getIndex(),
                        startRow + range.getIndex() + range.getRowspan() - 1,
                        8, 8));
            }
        }
    }

    /**
     * 数据类和合并信息类
     */
    private static class ExportDataResult {
        private final List<Map<String, Object>> rows;
        private final MergeInfo mergeInfo;

        public ExportDataResult(List<Map<String, Object>> rows, MergeInfo mergeInfo) {
            this.rows = rows;
            this.mergeInfo = mergeInfo;
        }

        public List<Map<String, Object>> getRows() { return rows; }
        public MergeInfo getMergeInfo() { return mergeInfo; }
    }

    private static class MergeInfo {
        private final List<MergeRange> indexMerges = new ArrayList<>();   // 序号列合并
        private final List<MergeRange> streetMerges = new ArrayList<>();  // 单位级合并
        private final List<MergeRange> projectMerges = new ArrayList<>(); // 项目级合并

        public void addIndexMerge(int index, int rowspan) {
            indexMerges.add(new MergeRange(index, rowspan));
        }

        public void addStreetMerge(int index, int rowspan) {
            streetMerges.add(new MergeRange(index, rowspan));
        }

        public void addProjectMerge(int index, int rowspan) {
            projectMerges.add(new MergeRange(index, rowspan));
        }

        public List<MergeRange> getIndexMerges() { return indexMerges; }
        public List<MergeRange> getStreetMerges() { return streetMerges; }
        public List<MergeRange> getProjectMerges() { return projectMerges; }
    }

    private static class MergeRange {
        private final int index;
        private final int rowspan;

        public MergeRange(int index, int rowspan) {
            this.index = index;
            this.rowspan = rowspan;
        }

        public int getIndex() { return index; }
        public int getRowspan() { return rowspan; }
    }

    
    //    @Override
//    public void exportCountLight(DeadlineNodeOutput req, HttpServletResponse response) {
//        JSONObject reqJson = new JSONObject();
//        reqJson.put("pageSize", 9999);
//        reqJson.put("pageNum", 1);
//        Answer<Object> objectAnswer = this.countLight(reqJson);
//
//        if (objectAnswer == null) {
//            throw new RuntimeException("查询接口返回空，请检查接口实现");
//        }
//        if (objectAnswer.getCode() != 200) {
//            throw new RuntimeException(String.format("分页查询失败：状态码=%d，提示=%s",
//                    objectAnswer.getCode(), objectAnswer.getMsg()));
//        }
//
//        Page<RectificationV2Vo> rectificationV2VoPage = (Page<RectificationV2Vo>) objectAnswer.getResult();
//        List<RectificationV2Vo> lightList = rectificationV2VoPage.getRecords();
//
//        List<LightVo> lightVos = new ArrayList<>();
//        if (lightList != null && !lightList.isEmpty())
//            for (RectificationV2Vo rectificationV2Vo : lightList) { // 变量名修正：避免与类名重复
//                List<RectificationInfo> rectificationInfos = rectificationV2Vo.getRectificationInfos();
//                if (rectificationInfos != null && !rectificationInfos.isEmpty())
//                    for (RectificationInfo rectificationInfo : rectificationInfos) {
//                        List<DeadlineNode> deadlineNodeList = rectificationInfo.getDeadlineNodeList();
//                        if (deadlineNodeList != null && !deadlineNodeList.isEmpty())
//                            for (DeadlineNode deadlineNode : deadlineNodeList) {
//                                LightVo lightVo = new LightVo();
//                                lightVo.setStreet(rectificationV2Vo.getStreet());
//                                lightVo.setName(deadlineNode.getName());
//                                lightVo.setOverdueTime(deadlineNode.getOverdueTime());
//                                lightVo.setTitle(rectificationInfo.getTitle());
//                                lightVo.setLeadLeader(rectificationInfo.getLeadLeader());
//                                lightVo.setPersonLiaible(rectificationInfo.getPersonLiaible());
//                                lightVo.setRate(rectificationV2Vo.getRate());
//                                lightVo.setRed(rectificationV2Vo.getRed());
//                                lightVo.setTotal(rectificationV2Vo.getTotal());
//                                lightVo.setProgressAndProblem(deadlineNode.getRemark());
//                                lightVo.setProjectType(rectificationInfo.getProjectChildCategoryType());
//                                lightVos.add(lightVo);
//                            }
//                    }
//            }
//
//        List<Map<String, Object>> rows = lightVos.stream().map(n -> {
//            Map<String, Object> m = new LinkedHashMap<>();
//            m.put("单位", n.getStreet());
//            m.put("项目数", n.getTotal());
//            m.put("红灯数", n.getRed());
//            m.put("红灯率", n.getRate());
//            m.put("红灯项目", n.getTitle());
//            m.put("牵头领导", n.getLeadLeader()); // 修正：原代码错用n.getRate()，此处改为正确的牵头领导字段
//            m.put("责任人", n.getPersonLiaible());
//            m.put("备注（项目类型）", n.getProjectType());
//            m.put("滞后环节", n.getName());
//            m.put("超期时间", n.getOverdueTime());
//            m.put("滞后原因", n.getProgressAndProblem());
//            return m;
//        }).collect(Collectors.toList());
//
//        String fileName = "项目列表导出" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + ".xlsx";
//        ExcelWriter writer = ExcelUtil.getBigWriter();
//        try {
//            LinkedHashMap<String, String> header = new LinkedHashMap<>();
//            header.put("单位", "单位");
//            header.put("项目数", "项目数");
//            header.put("红灯数", "红灯数");
//            header.put("红灯率", "红灯率");
//            header.put("红灯项目", "红灯项目");
//            header.put("牵头领导", "牵头领导");
//            header.put("责任人", "责任人");
//            header.put("备注（项目类型）", "备注（项目类型）");
//            header.put("滞后环节", "滞后环节");
//            header.put("超期时间", "超期时间");
//            header.put("滞后原因", "滞后原因");
//
//
//            header.forEach(writer::addHeaderAlias);
//            writer.setOnlyAlias(true);
//
//            String title = "红灯项目情况统计";
//            writer.merge(header.size() - 1, title);
//
//            Workbook wb = writer.getWorkbook();
//            Sheet sheet = writer.getSheet();
//            Row r0 = sheet.getRow(0) != null ? sheet.getRow(0) : sheet.createRow(0);
//            Cell c0 = r0.getCell(0) != null ? r0.getCell(0) : r0.createCell(0);
//            CellStyle titleStyle = wb.createCellStyle();
//            titleStyle.setAlignment(HorizontalAlignment.CENTER);
//            titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
//            Font titleFont = wb.createFont();
//            titleFont.setBold(true);
//            titleFont.setFontHeightInPoints((short) 12);
//            titleStyle.setFont(titleFont);
//            c0.setCellStyle(titleStyle);
//            r0.setHeightInPoints(22);
//
//            sheet.createFreezePane(0, 2); // 冻结标题行+表头行
//
//            // 创建自动换行的单元格样式
//            CellStyle wrapStyle = wb.createCellStyle();
//            wrapStyle.setWrapText(true);
//
//            // 写入数据（数据从第2行开始：0=标题，1=表头）
//            writer.write(rows, true);
//
//            // 设置列宽
//            int i = 0;
//            for (String ignored : header.keySet()) {
//                writer.setColumnWidth(i, 20);
//                if (i == 1) { // 第2列（索引1）应用自动换行
//                    sheet.setDefaultColumnStyle(i, wrapStyle);
//                }
//                i++;
//            }
//
//            // 输出Excel文件
//            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
//            response.setHeader("Content-Disposition",
//                    "attachment;filename=" + URLEncoder.encode(fileName, java.nio.charset.StandardCharsets.UTF_8.toString()));
//            ServletOutputStream out = response.getOutputStream();
//            writer.flush(out, true);
//            out.flush();
//        } catch (Exception e) {
//            throw new RuntimeException("导出失败", e);
//        } finally {
//            writer.close(); // 关闭流，释放资源
//        }
//    }

    @Override
    public void exportLightRectification(DeadlineNodeOutput req, HttpServletResponse response) {
        JSONObject reqJson = new JSONObject();
        reqJson.put("pageSize", 9999);
        reqJson.put("pageNum", 1);
        Answer<Object> objectAnswer = this.lightRectification(reqJson);

        if (objectAnswer == null) {
            throw new RuntimeException("查询接口返回空，请检查接口实现");
        }
        if (objectAnswer.getCode() != 200) {
            throw new RuntimeException(String.format("分页查询失败：状态码=%d，提示=%s",
                    objectAnswer.getCode(), objectAnswer.getMsg()));
        }

        Page<RectificationInfo> rectificationInfoPage = (Page<RectificationInfo>) objectAnswer.getResult();
        List<RectificationInfo> lightRectification = rectificationInfoPage.getRecords();

        List<LightRectification> lightRectificationList = new ArrayList<>();
        for (RectificationInfo rectificationInfo : lightRectification) {
            List<DeadlineNode> deadlineNodeList = rectificationInfo.getDeadlineNodeList();
            for (DeadlineNode deadlineNode : deadlineNodeList) {
                LightRectification lightRectification1 = new LightRectification();
                lightRectification1.setName(deadlineNode.getName());
                lightRectification1.setDeadlineRectificationPeriod(deadlineNode.getDeadlineRectificationPeriod());
                lightRectification1.setUpdateReason(deadlineNode.getUpdateReason());
                lightRectification1.setDeadline(deadlineNode.getDeadline());
                lightRectification1.setTitle(rectificationInfo.getTitle());
                lightRectification1.setDepartment(rectificationInfo.getDepartmentName());
                lightRectification1.setProgressAndProblem(rectificationInfo.getProgressAndProblem());
                if (StringUtils.isNotBlank(rectificationInfo.getProjectChildCategoryType())) {
                    lightRectification1.setType(rectificationInfo.getProjectChildCategoryType());
                } else if (StringUtils.isNotBlank(rectificationInfo.getProjectChildType())) {
                    lightRectification1.setType(rectificationInfo.getProjectChildType());
                } else {
                    lightRectification1.setType(rectificationInfo.getProjectType());
                }
                lightRectificationList.add(lightRectification1);
            }
        }

        List<Map<String, Object>> rows = lightRectificationList.stream().map(n -> {
            Map<String, Object> m = new LinkedHashMap<>();
            m.put("项目名称", n.getTitle());
            m.put("类别", n.getType());
            m.put("牵头单位", n.getDepartment());
            m.put("工作进展及存在问题", n.getProgressAndProblem());
            m.put("主要滞后环节", n.getName());
            m.put("修改原因", n.getUpdateReason());

            // 定义日期格式化器（全局复用更佳，此处简化示例）
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

            // 处理原开工时间（Date类型 -> 字符串，非空判断）
            Date originalDeadline = n.getDeadline(); // 假设getDeadline()返回Date类型
            m.put("原开工时间", originalDeadline != null ? sdf.format(originalDeadline) : null);

            // 处理修改后开工时间（Date类型 -> 字符串，非空判断）
            Date rectificationPeriod = n.getDeadlineRectificationPeriod(); // 假设返回Date类型
            m.put("修改后开工时间", rectificationPeriod != null ? sdf.format(rectificationPeriod) : null);

            return m;
        }).collect(Collectors.toList());

        String fileName = "项目列表导出" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + ".xlsx";
        ExcelWriter writer = ExcelUtil.getBigWriter();
        try {
            LinkedHashMap<String, String> header = new LinkedHashMap<>();
            header.put("项目名称", "项目名称");
            header.put("类别", "类别");
            header.put("牵头单位", "牵头单位");
            header.put("工作进展及存在问题", "工作进展及存在问题");
            header.put("主要滞后环节", "主要滞后环节");
            header.put("修改原因", "修改原因");
            header.put("原开工时间", "原开工时间");
            header.put("修改后开工时间", "修改后开工时间");

            header.forEach(writer::addHeaderAlias);
            writer.setOnlyAlias(true);

            String title = "红灯项目整改情况统计";
            writer.merge(header.size() - 1, title);

            Workbook wb = writer.getWorkbook();
            Sheet sheet = writer.getSheet();
            Row r0 = sheet.getRow(0) != null ? sheet.getRow(0) : sheet.createRow(0);
            Cell c0 = r0.getCell(0) != null ? r0.getCell(0) : r0.createCell(0);
            CellStyle titleStyle = wb.createCellStyle();
            titleStyle.setAlignment(HorizontalAlignment.CENTER);
            titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            Font titleFont = wb.createFont();
            titleFont.setBold(true);
            titleFont.setFontHeightInPoints((short) 12);
            titleStyle.setFont(titleFont);
            c0.setCellStyle(titleStyle);
            r0.setHeightInPoints(22);

            // 冻结表头（标题行+表头行，共2行）
            sheet.createFreezePane(0, 2);

            // 创建自动换行的单元格样式
            CellStyle wrapStyle = wb.createCellStyle();
            wrapStyle.setWrapText(true);

            // 写入数据（此时数据从第2行开始，0行为标题，1行为表头）
            writer.write(rows, true);

            // 设置列宽
            int i = 0;
            for (String ignored : header.keySet()) {
                writer.setColumnWidth(i, 20);
                if (i == 1) { // 第2列（索引1）应用自动换行
                    sheet.setDefaultColumnStyle(i, wrapStyle);
                }
                i++;
            }

            // -------------------------- 核心：合并上下内容相同的单元格（空内容不合并） --------------------------
            int dataStartRow = 2; // 数据从第2行（索引2）开始
            int rowCount = sheet.getLastRowNum(); // 最后一行索引
            int columnCount = header.size() - 4; // 列数

            // 按列处理合并
            for (int col = 0; col < columnCount; col++) {
                int mergeStart = dataStartRow; // 记录当前合并区间的起始行
                for (int row = dataStartRow + 1; row <= rowCount; row++) {
                    // 获取当前行和上一行的单元格内容（已处理空值，空内容返回""）
                    String currentVal = getCellValue(sheet, row, col);
                    String prevVal = getCellValue(sheet, row - 1, col);

                    // 新增判断：若当前内容为空，直接跳过合并，更新起始行为当前行
                    if (currentVal.isEmpty()) {
                        mergeStart = row;
                        continue;
                    }

                    // 原逻辑：内容不同或到最后一行，且合并区间长度>1时才合并
                    if (!Objects.equals(currentVal, prevVal) || row == rowCount) {
                        // 额外判断：上一行内容不为空（避免空内容参与合并）
                        if (row - 1 > mergeStart && !prevVal.isEmpty()) {
                            sheet.addMergedRegion(new CellRangeAddress(mergeStart, row - 1, col, col));
                        }
                        mergeStart = row; // 更新起始行为当前行
                    }
                }
            }
            // ------------------------------------------------------------------------------

            // 输出Excel
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + URLEncoder.encode(fileName, java.nio.charset.StandardCharsets.UTF_8.toString()));
            ServletOutputStream out = response.getOutputStream();
            writer.flush(out, true);
            out.flush();
        } catch (Exception e) {
            throw new RuntimeException("导出失败", e);
        } finally {
            writer.close();
        }
    }

    // 辅助方法：获取单元格内容（空单元格返回""，统一处理不同单元格类型）
    private String getCellValue(Sheet sheet, int rowIndex, int colIndex) {
        Row row = sheet.getRow(rowIndex);
        if (row == null) {
            return ""; // 行不存在视为空内容
        }
        Cell cell = row.getCell(colIndex);
        if (cell == null) {
            return ""; // 单元格不存在视为空内容
        }

        // 处理不同类型的单元格值，空字符串返回""
        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue().trim().isEmpty() ? "" : cell.getStringCellValue().trim();
            case NUMERIC:
                if (HSSFDateUtil.isCellDateFormatted(cell)) {
                    // 日期类型：用Hutool格式化，避免默认格式杂乱
                    String dateStr = DateUtil.format(cell.getDateCellValue(), "yyyy-MM-dd");
                    return dateStr.isEmpty() ? "" : dateStr;
                } else {
                    // 数字类型：转字符串后去空
                    String numStr = String.valueOf(cell.getNumericCellValue()).trim();
                    // 处理数字末尾的".0"（如123.0→123）
                    return numStr.endsWith(".0") ? numStr.replace(".0", "") : numStr;
                }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                // 公式单元格：取计算结果（若计算失败返回公式本身）
                try {
                    return cell.getStringCellValue().trim().isEmpty() ? "" : cell.getStringCellValue().trim();
                } catch (Exception e) {
                    return cell.getCellFormula().trim().isEmpty() ? "" : cell.getCellFormula().trim();
                }
            default:
                return ""; // 其他类型视为空内容
        }
    }

    @Override
    public Answer<Object> countAmount(String projectStatus) {
        // 1. 查询原始数据
        List<CountAmount> countAmountList = rectificationInfoMapper.getCountAmount(projectStatus);
        if (countAmountList == null) {
            countAmountList = new ArrayList<>();
        }

        // 2. 过滤无效数据（三个计数不同时为0）
        List<CountAmount> filteredList = new ArrayList<>();
        for (CountAmount data : countAmountList) {
            Long nCount = data.getCountRectificationN() == null ? 0 : data.getCountRectificationN();
            Long mCount = data.getCountRectificationM() == null ? 0 : data.getCountRectificationM();
            Long zCount = data.getCountRectificationZ() == null ? 0 : data.getCountRectificationZ();

            if (!(nCount == 0 && mCount == 0 && zCount == 0)) {
                filteredList.add(data);
            }
        }

        // 3. 排序（按预设顺序）
        filteredList.sort((data1, data2) -> {
            String name1 = data1.getDepartment();
            String name2 = data2.getDepartment();
            int index1 = AREA_ORDER.indexOf(name1);
            int index2 = AREA_ORDER.indexOf(name2);

            if (index1 == -1 && index2 == -1) {
                return name1.compareTo(name2);
            } else if (index1 == -1) {
                return 1;
            } else if (index2 == -1) {
                return -1;
            } else {
                return Integer.compare(index1, index2);
            }
        });

        // 4. 计算各项总计（新增逻辑）
        NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US); // 处理带千分位的数字字符串
        BigDecimal countRectificationNTotal = BigDecimal.ZERO;
        BigDecimal sumAllAmountNTotal = BigDecimal.ZERO;
        BigDecimal countOver10BillionNTotal = BigDecimal.ZERO;
        BigDecimal sumOver10BillionNTotal = BigDecimal.ZERO;

        BigDecimal countRectificationZTotal = BigDecimal.ZERO;
        BigDecimal sumAllAmountZTotal = BigDecimal.ZERO;
        BigDecimal countOver10BillionZTotal = BigDecimal.ZERO;
        BigDecimal sumOver10BillionZTotal = BigDecimal.ZERO;

        BigDecimal countRectificationMTotal = BigDecimal.ZERO;
        BigDecimal sumAllAmountMTotal = BigDecimal.ZERO;
        BigDecimal countOver10BillionMTotal = BigDecimal.ZERO;
        BigDecimal sumOver10BillionMTotal = BigDecimal.ZERO;

        for (CountAmount data : filteredList) {
            // 拟开工项目合计
            countRectificationNTotal = countRectificationNTotal.add(
                    data.getCountRectificationN() != null ? new BigDecimal(data.getCountRectificationN()) : BigDecimal.ZERO
            );
            sumAllAmountNTotal = sumAllAmountNTotal.add(
                    BigDecimalUtils.convertStringToBigDecimal(data.getSumAllAmountN(), numberFormat)
            );
            countOver10BillionNTotal = countOver10BillionNTotal.add(
                    data.getCountOver10BillionN() != null ? new BigDecimal(data.getCountOver10BillionN()) : BigDecimal.ZERO
            );
            sumOver10BillionNTotal = sumOver10BillionNTotal.add(
                    BigDecimalUtils.convertStringToBigDecimal(data.getSumOver10BillionN(), numberFormat)
            );

            // 在建项目合计
            countRectificationZTotal = countRectificationZTotal.add(
                    data.getCountRectificationZ() != null ? new BigDecimal(data.getCountRectificationZ()) : BigDecimal.ZERO
            );
            sumAllAmountZTotal = sumAllAmountZTotal.add(
                    BigDecimalUtils.convertStringToBigDecimal(data.getSumAllAmountZ(), numberFormat)
            );
            countOver10BillionZTotal = countOver10BillionZTotal.add(
                    data.getCountOver10BillionZ() != null ? new BigDecimal(data.getCountOver10BillionZ()) : BigDecimal.ZERO
            );
            sumOver10BillionZTotal = sumOver10BillionZTotal.add(
                    BigDecimalUtils.convertStringToBigDecimal(data.getSumOver10BillionZ(), numberFormat)
            );

            // 谋划推进项目合计
            countRectificationMTotal = countRectificationMTotal.add(
                    data.getCountRectificationM() != null ? new BigDecimal(data.getCountRectificationM()) : BigDecimal.ZERO
            );
            sumAllAmountMTotal = sumAllAmountMTotal.add(
                    BigDecimalUtils.convertStringToBigDecimal(data.getSumAllAmountM(), numberFormat)
            );
            countOver10BillionMTotal = countOver10BillionMTotal.add(
                    data.getCountOver10BillionM() != null ? new BigDecimal(data.getCountOver10BillionM()) : BigDecimal.ZERO
            );
            sumOver10BillionMTotal = sumOver10BillionMTotal.add(
                    BigDecimalUtils.convertStringToBigDecimal(data.getSumOver10BillionM(), numberFormat)
            );
        }

        // 5. 封装到CountAmount2TotalVo
        CountAmount2TotalVo resultVo = CountAmount2TotalVo.builder()
                .dataList(filteredList)  // 各单位明细数据
                // 拟开工合计
                .countRectificationNTotal(countRectificationNTotal.longValue())
                .sumAllAmountNTotal(BigDecimalUtils.formatBigDecimalToString(sumAllAmountNTotal, true))
                .countOver10BillionNTotal(countOver10BillionNTotal.longValue())
                .sumOver10BillionNTotal(BigDecimalUtils.formatBigDecimalToString(sumOver10BillionNTotal, true))
                // 在建合计
                .countRectificationZTotal(countRectificationZTotal.longValue())
                .sumAllAmountZTotal(BigDecimalUtils.formatBigDecimalToString(sumAllAmountZTotal, true))
                .countOver10BillionZTotal(countOver10BillionZTotal.longValue())
                .sumOver10BillionZTotal(BigDecimalUtils.formatBigDecimalToString(sumOver10BillionZTotal, true))
                // 谋划推进合计
                .countRectificationMTotal(countRectificationMTotal.longValue())
                .sumAllAmountMTotal(BigDecimalUtils.formatBigDecimalToString(sumAllAmountMTotal, true))
                .countOver10BillionMTotal(countOver10BillionMTotal.longValue())
                .sumOver10BillionMTotal(BigDecimalUtils.formatBigDecimalToString(sumOver10BillionMTotal, true))
                .build();

        // 6. 返回封装后的结果
        return Answer.ok(resultVo);
    }

    @Override
    public Answer<Object> countAmountInvestment(String projectStatus) {
        List<CountAmountInvestment> countAmountInvestmentList = rectificationInfoMapper.countAmountInvestment(projectStatus);

        List<CountAmountInvestment> filteredList = new ArrayList<>();
        for (CountAmountInvestment data : countAmountInvestmentList) {
            // 假设字段为 Integer 类型，若为其他数值类型（如 Long），判断逻辑一致
            Long wCount = data.getCountRectificationW() == null ? 0 : data.getCountRectificationW();
            Long dCount = data.getCountRectificationD() == null ? 0 : data.getCountRectificationD();
            // 仅保留 不同时为 0 的数据
            if (!(wCount == 0 && dCount == 0)) {
                filteredList.add(data);
            }
        }

        filteredList.sort((data1, data2) -> {
            String name1 = data1.getDepartment();
            String name2 = data2.getDepartment();
            int index1 = AREA_ORDER.indexOf(name1);
            int index2 = AREA_ORDER.indexOf(name2);

            if (index1 == -1 && index2 == -1) {
                return name1.compareTo(name2);
            } else if (index1 == -1) {
                return 1;
            } else if (index2 == -1) {
                return -1;
            } else {
                return Integer.compare(index1, index2); // 修正原代码 Integerco 拼写错误
            }
        });

        // 补全“白莲河示范区”（即使数据为0）
        // 1. 检查现有列表中是否已存在“白莲河示范区”
        boolean hasBailianhe = filteredList.stream()
                .anyMatch(data -> "白莲河示范区".equals(data.getDepartment()));

        // 2. 若不存在，则添加一条数据（各数值字段为0）
        if (!hasBailianhe) {
            CountAmountInvestment bailianheData = new CountAmountInvestment();
            bailianheData.setDepartment("白莲河示范区");
            // 设置所有数值字段为0（根据实际类字段补充，这里假设核心字段为以下两个）
            bailianheData.setCountRectificationW(0L);
            bailianheData.setSumAllAmountW("0.00");
            bailianheData.setCountOver10BillionW(0L);
            bailianheData.setSumOver10BillionW("0.00");
            bailianheData.setCountRectificationD(0L);
            bailianheData.setSumAllAmountD("0.00");
            bailianheData.setCountOver10BillionD(0L);
            bailianheData.setSumOver10BillionD("0.00");
            // 3. 将新增数据插入到正确的位置（按AREA_ORDER顺序）
            int targetIndex = AREA_ORDER.indexOf("白莲河示范区");
            // 找到插入位置：在列表中找到第一个索引大于targetIndex的元素位置
            int insertIndex = 0;
            while (insertIndex < filteredList.size()) {
                String dept = filteredList.get(insertIndex).getDepartment();
                int deptIndex = AREA_ORDER.indexOf(dept);
                if (deptIndex > targetIndex) {
                    break;
                }
                insertIndex++;
            }
            // 插入到对应位置
            filteredList.add(insertIndex, bailianheData);
        }
        return Answer.ok(filteredList);
    }



    @Override
    public Answer<Object> countAmountBefore(JSONObject reqJson) {
        String startTime = reqJson.getString("startTime");
        String endTime = reqJson.getString("endTime");
        String projectStatus = reqJson.getString("projectStatus"); // 默认 不包含已销号, 已销号 包含已销号;
        String projectChildCategoryType = reqJson.getString("projectChildCategoryType");

        // 1. 查询所有部门的基础数据
        List<CountAmountBefore> countAmountBeforeList = rectificationInfoMapper.countAmountBefore(startTime, endTime, projectChildCategoryType, projectStatus);

        // 2. 统计所有部门的「总计值」（用于计算占比的分母）
        long totalCountRectification = 0; // 总个数（所有部门countRectification之和）
        double totalSumAllAmount = 0.0;   // 总投资（所有部门sumAllAmount之和）
        long totalCountCancelled = 0;     // 总销号个数（所有部门countCancelled之和）
        double totalSumCancelledInvestment = 0.0; // 总销号投资（所有部门sumCancelledInvestment之和）

        // 2.1 累加总计值（注意：sumAllAmount和sumCancelledInvestment是String类型，需转为double）
        for (CountAmountBefore item : countAmountBeforeList) {
            // 累加个数（非空判断）
            if (item.getCountRectification() != null) {
                totalCountRectification += item.getCountRectification();
            }
            // 累加总投资（非空+格式转换）
            if (item.getSumAllAmount() != null && !item.getSumAllAmount().trim().isEmpty()) {
                try {
                    totalSumAllAmount += Double.parseDouble(item.getSumAllAmount());
                } catch (NumberFormatException e) {
                    // 若投资金额格式异常，按0处理（可根据业务调整日志打印）
                }
            }
            // 累加已销号个数（非空判断）
            if (item.getCountCancelled() != null) {
                totalCountCancelled += item.getCountCancelled();
            }
            // 累加已销号总投资（非空+格式转换）
            if (item.getSumCancelledInvestment() != null && !item.getSumCancelledInvestment().trim().isEmpty()) {
                try {
                    totalSumCancelledInvestment += Double.parseDouble(item.getSumCancelledInvestment());
                } catch (NumberFormatException e) {
                }
            }
        }

        // 3. 遍历每个部门，计算四个占比（当前部门值 / 总计值 * 100，保留1位小数）
        for (CountAmountBefore item : countAmountBeforeList) {
            // 3.1 个数占比：countRectificationRatio = 部门个数 / 总个数 * 100
            if (totalCountRectification > 0 && item.getCountRectification() != null) {
                double ratio = (double) item.getCountRectification() / totalCountRectification * 100;
                item.setCountRectificationRatio(String.format("%.1f", ratio)+"%");
            } else {
                item.setCountRectificationRatio("0.0"+"%"); // 分母为0或数据为空时，占比默认0.0
            }

            // 3.2 总投资占比：sumAllAmountRatio = 部门总投资 / 总投资 * 100
            if (totalSumAllAmount > 0.01 && item.getSumAllAmount() != null && !item.getSumAllAmount().trim().isEmpty()) {
                try {
                    double deptAmount = Double.parseDouble(item.getSumAllAmount());
                    double ratio = deptAmount / totalSumAllAmount * 100;
                    item.setSumAllAmountRatio(String.format("%.1f", ratio)+"%");
                } catch (NumberFormatException e) {
                    item.setSumAllAmountRatio("0.0"+"%");
                }
            } else {
                item.setSumAllAmountRatio("0.0"+"%");
            }

            // 3.3 已销号个数占比：countCancelledRatio = 部门销号个数 / 总销号个数 * 100
            if (totalCountCancelled > 0 && item.getCountCancelled() != null) {
                double ratio = (double) item.getCountCancelled() / totalCountCancelled * 100;
                item.setCountCancelledRatio(String.format("%.1f", ratio)+"%");
            } else {
                item.setCountCancelledRatio("0.0"+"%");
            }

            // 3.4 已销号总投资占比：sumCancelledInvestmentRatio = 部门销号投资 / 总销号投资 * 100
            if (totalSumCancelledInvestment > 0.01 && item.getSumCancelledInvestment() != null && !item.getSumCancelledInvestment().trim().isEmpty()) {
                try {
                    double deptCancelledAmount = Double.parseDouble(item.getSumCancelledInvestment());
                    double ratio = deptCancelledAmount / totalSumCancelledInvestment * 100;
                    item.setSumCancelledInvestmentRatio(String.format("%.1f", ratio)+"%");
                } catch (NumberFormatException e) {
                    item.setSumCancelledInvestmentRatio("0.0%");
                }
            } else {
                item.setSumCancelledInvestmentRatio("0.0%");
            }
        }

        // 4. 保持原有排序逻辑（按AREA_ORDER顺序排序）
        countAmountBeforeList.sort((data1, data2) -> {
            String name1 = data1.getDepartment();
            String name2 = data2.getDepartment();
            // 获取两个区域在排序列表中的索引
            int index1 = AREA_ORDER.indexOf(name1);
            int index2 = AREA_ORDER.indexOf(name2);
            // 索引小的排前面；未在列表中的区域放最后（按名称自然排序）
            if (index1 == -1 && index2 == -1) {
                return name1.compareTo(name2); // 均不在列表中，按名称排序
            } else if (index1 == -1) {
                return 1; // 前者不在列表中，放后面
            } else if (index2 == -1) {
                return -1; // 后者不在列表中，放前面
            } else {
                return Integer.compare(index1, index2); // 按索引顺序排序
            }
        });

        return Answer.ok(countAmountBeforeList);
    }
//    public Answer<Object> countAmountBefore(JSONObject reqJson) {
//        String startTime = reqJson.getString("startTime");
//        String endTime = reqJson.getString("endTime");
//        String projectStatus = reqJson.getString("projectStatus"); // 默认 不包含已销号, 已销号 包含已销号;
//        String projectChildCategoryType = reqJson.getString("projectChildCategoryType");
//        List<CountAmountBefore> countAmountBeforeList = rectificationInfoMapper.countAmountBefore(startTime,endTime, projectChildCategoryType,projectStatus);
//        countAmountBeforeList.sort((data1, data2) -> {
//            String name1 = data1.getDepartment();
//            String name2 = data2.getDepartment();
//            // 获取两个区域在排序列表中的索引
//            int index1 = AREA_ORDER.indexOf(name1);
//            int index2 = AREA_ORDER.indexOf(name2);
//            // 索引小的排前面；未在列表中的区域放最后（按名称自然排序）
//            if (index1 == -1 && index2 == -1) {
//                return name1.compareTo(name2); // 均不在列表中，按名称排序
//            } else if (index1 == -1) {
//                return 1; // 前者不在列表中，放后面
//            } else if (index2 == -1) {
//                return -1; // 后者不在列表中，放后面
//            } else {
//                return Integer.compare(index1, index2); // 按索引顺序排序
//            }
//        });
//        return Answer.ok(countAmountBeforeList);
//    }


    @Value("${excel.template.count-light:template/countAmount.xlsx}")
    private String countAmountTemplatePath;

    /**
     * 导出平台项目统计表
     */
    @SneakyThrows  // 若项目允许，可使用Lombok简化异常声明（或显式throws IOException）
    public void exportCountAmount(DeadlineNodeOutput req, HttpServletResponse response, JSONObject reqJson) {
        String projectStatus = reqJson.getString("projectStatus");
        // 1. 获取数据（修改为CountAmount2TotalVo类型）
        Answer<Object> objectAnswer = this.countAmount(projectStatus);
        Assert.notNull(objectAnswer, "统计数据查询结果为空");
        Assert.isTrue(objectAnswer.getResult() instanceof CountAmount2TotalVo, "统计结果格式错误，应为CountAmount2TotalVo类型");

        // 从返回结果中获取封装的VO对象
        CountAmount2TotalVo totalVo = (CountAmount2TotalVo) objectAnswer.getResult();
        List<CountAmount> countAmountList = totalVo.getDataList(); // 明细数据列表
        Assert.notEmpty(countAmountList, "统计数据列表为空");

        // 2. 从totalVo中直接获取总计数据（无需重复计算）
        // 拟开工合计
        Long countRectificationNTotal = totalVo.getCountRectificationNTotal();
        String sumAllAmountNTotalStr = totalVo.getSumAllAmountNTotal();
        Long countOver10BillionNTotal = totalVo.getCountOver10BillionNTotal();
        String sumOver10BillionNTotalStr = totalVo.getSumOver10BillionNTotal();

        // 在建合计
        Long countRectificationZTotal = totalVo.getCountRectificationZTotal();
        String sumAllAmountZTotalStr = totalVo.getSumAllAmountZTotal();
        Long countOver10BillionZTotal = totalVo.getCountOver10BillionZTotal();
        String sumOver10BillionZTotalStr = totalVo.getSumOver10BillionZTotal();

        // 谋划推进合计
        Long countRectificationMTotal = totalVo.getCountRectificationMTotal();
        String sumAllAmountMTotalStr = totalVo.getSumAllAmountMTotal();
        Long countOver10BillionMTotal = totalVo.getCountOver10BillionMTotal();
        String sumOver10BillionMTotalStr = totalVo.getSumOver10BillionMTotal();

        // 3. 转换总计为BigDecimal（用于占比计算）
        NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US);
        BigDecimal sumAllAmountNTotal = BigDecimalUtils.convertStringToBigDecimal(sumAllAmountNTotalStr, numberFormat);
        BigDecimal sumOver10BillionNTotal = BigDecimalUtils.convertStringToBigDecimal(sumOver10BillionNTotalStr, numberFormat);
        BigDecimal sumAllAmountZTotal = BigDecimalUtils.convertStringToBigDecimal(sumAllAmountZTotalStr, numberFormat);
        BigDecimal sumOver10BillionZTotal = BigDecimalUtils.convertStringToBigDecimal(sumOver10BillionZTotalStr, numberFormat);
        BigDecimal sumAllAmountMTotal = BigDecimalUtils.convertStringToBigDecimal(sumAllAmountMTotalStr, numberFormat);
        BigDecimal sumOver10BillionMTotal = BigDecimalUtils.convertStringToBigDecimal(sumOver10BillionMTotalStr, numberFormat);

        // 4. 计算每条数据的占比（新增逻辑）
        for (CountAmount countAmount : countAmountList) {
            if (countAmount == null) {
                continue;
            }

            // 拟开工重点项目占比
            // 个数占比 = 本条个数 / 总个数 * 100%
            if (countRectificationNTotal != null && countRectificationNTotal > 0 && countAmount.getCountRectificationN() != null) {
                BigDecimal ratio = new BigDecimal(countAmount.getCountRectificationN())
                        .divide(new BigDecimal(countRectificationNTotal), 4, RoundingMode.HALF_UP)
                        .multiply(new BigDecimal(100));
                countAmount.setCountRectificationNRatio(String.format("%.1f%%", ratio));
            } else {
                countAmount.setCountRectificationNRatio("0.0%");
            }

            // 总投资占比
            if (sumAllAmountNTotal.compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal sumAllAmountN = BigDecimalUtils.convertStringToBigDecimal(countAmount.getSumAllAmountN(), numberFormat);
                BigDecimal ratio = sumAllAmountN
                        .divide(sumAllAmountNTotal, 4, RoundingMode.HALF_UP)
                        .multiply(new BigDecimal(100));
                countAmount.setSumAllAmountNRatio(String.format("%.1f%%", ratio));
            } else {
                countAmount.setSumAllAmountNRatio("0.0%");
            }

            // 10亿以上拟开工个数占比
            if (countOver10BillionNTotal != null && countOver10BillionNTotal > 0 && countAmount.getCountOver10BillionN() != null) {
                BigDecimal ratio = new BigDecimal(countAmount.getCountOver10BillionN())
                        .divide(new BigDecimal(countOver10BillionNTotal), 4, RoundingMode.HALF_UP)
                        .multiply(new BigDecimal(100));
                countAmount.setCountOver10BillionNRatio(String.format("%.1f%%", ratio));
            } else {
                countAmount.setCountOver10BillionNRatio("0.0%");
            }

            // 10亿以上拟开工投资占比
            if (sumOver10BillionNTotal.compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal sumOver10BillionN = BigDecimalUtils.convertStringToBigDecimal(countAmount.getSumOver10BillionN(), numberFormat);
                BigDecimal ratio = sumOver10BillionN
                        .divide(sumOver10BillionNTotal, 4, RoundingMode.HALF_UP)
                        .multiply(new BigDecimal(100));
                countAmount.setSumOver10BillionNRatio(String.format("%.1f%%", ratio));
            } else {
                countAmount.setSumOver10BillionNRatio("0.0%");
            }

            // 在建重点项目占比（同拟开工逻辑）
            // 个数占比
            if (countRectificationZTotal != null && countRectificationZTotal > 0 && countAmount.getCountRectificationZ() != null) {
                BigDecimal ratio = new BigDecimal(countAmount.getCountRectificationZ())
                        .divide(new BigDecimal(countRectificationZTotal), 4, RoundingMode.HALF_UP)
                        .multiply(new BigDecimal(100));
                countAmount.setCountRectificationZRatio(String.format("%.1f%%", ratio));
            } else {
                countAmount.setCountRectificationZRatio("0.0%");
            }

            // 总投资占比（在建）
            if (sumAllAmountZTotal.compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal sumAllAmountZ = BigDecimalUtils.convertStringToBigDecimal(countAmount.getSumAllAmountZ(), numberFormat);
                BigDecimal ratio = sumAllAmountZ
                        .divide(sumAllAmountZTotal, 4, RoundingMode.HALF_UP)
                        .multiply(new BigDecimal(100));
                countAmount.setSumAllAmountZRatio(String.format("%.1f%%", ratio));
            } else {
                countAmount.setSumAllAmountZRatio("0.0%");
            }

            // 10亿以上在建个数占比
            if (countOver10BillionZTotal != null && countOver10BillionZTotal > 0 && countAmount.getCountOver10BillionZ() != null) {
                BigDecimal ratio = new BigDecimal(countAmount.getCountOver10BillionZ())
                        .divide(new BigDecimal(countOver10BillionZTotal), 4, RoundingMode.HALF_UP)
                        .multiply(new BigDecimal(100));
                countAmount.setCountOver10BillionZRatio(String.format("%.1f%%", ratio));
            } else {
                countAmount.setCountOver10BillionZRatio("0.0%");
            }

            // 10亿以上在建投资占比
            if (sumOver10BillionZTotal.compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal sumOver10BillionZ = BigDecimalUtils.convertStringToBigDecimal(countAmount.getSumOver10BillionZ(), numberFormat);
                BigDecimal ratio = sumOver10BillionZ
                        .divide(sumOver10BillionZTotal, 4, RoundingMode.HALF_UP)
                        .multiply(new BigDecimal(100));
                countAmount.setSumOver10BillionZRatio(String.format("%.1f%%", ratio));
            } else {
                countAmount.setSumOver10BillionZRatio("0.0%");
            }

            // 谋划推进项目占比（同拟开工逻辑）
            // 个数占比
            if (countRectificationMTotal != null && countRectificationMTotal > 0 && countAmount.getCountRectificationM() != null) {
                BigDecimal ratio = new BigDecimal(countAmount.getCountRectificationM())
                        .divide(new BigDecimal(countRectificationMTotal), 4, RoundingMode.HALF_UP)
                        .multiply(new BigDecimal(100));
                countAmount.setCountRectificationMRatio(String.format("%.1f%%", ratio));
            } else {
                countAmount.setCountRectificationMRatio("0.0%");
            }

            // 总投资占比（谋划推进）
            if (sumAllAmountMTotal.compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal sumAllAmountM = BigDecimalUtils.convertStringToBigDecimal(countAmount.getSumAllAmountM(), numberFormat);
                BigDecimal ratio = sumAllAmountM
                        .divide(sumAllAmountMTotal, 4, RoundingMode.HALF_UP)
                        .multiply(new BigDecimal(100));
                countAmount.setSumAllAmountMRatio(String.format("%.1f%%", ratio));
            } else {
                countAmount.setSumAllAmountMRatio("0.0%");
            }

            // 10亿以上谋划推进个数占比
            if (countOver10BillionMTotal != null && countOver10BillionMTotal > 0 && countAmount.getCountOver10BillionM() != null) {
                BigDecimal ratio = new BigDecimal(countAmount.getCountOver10BillionM())
                        .divide(new BigDecimal(countOver10BillionMTotal), 4, RoundingMode.HALF_UP)
                        .multiply(new BigDecimal(100));
                countAmount.setCountOver10BillionMRatio(String.format("%.1f%%", ratio));
            } else {
                countAmount.setCountOver10BillionMRatio("0.0%");
            }

            // 10亿以上谋划推进投资占比
            if (sumOver10BillionMTotal.compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal sumOver10BillionM = BigDecimalUtils.convertStringToBigDecimal(countAmount.getSumOver10BillionM(), numberFormat);
                BigDecimal ratio = sumOver10BillionM
                        .divide(sumOver10BillionMTotal, 4, RoundingMode.HALF_UP)
                        .multiply(new BigDecimal(100));
                countAmount.setSumOver10BillionMRatio(String.format("%.1f%%", ratio));
            } else {
                countAmount.setSumOver10BillionMRatio("0.0%");
            }
        }

        // 5. 构建导出用的合计VO（使用totalVo中的总计数据）
        ExportCountAmountTotalVo exportTotalVo = new ExportCountAmountTotalVo();
        exportTotalVo.setCountRectificationNTotal(countRectificationNTotal);
        exportTotalVo.setSumAllAmountNTotal(sumAllAmountNTotalStr);
        exportTotalVo.setCountOver10BillionNTotal(countOver10BillionNTotal);
        exportTotalVo.setSumOver10BillionNTotal(sumOver10BillionNTotalStr);

        exportTotalVo.setCountRectificationZTotal(countRectificationZTotal);
        exportTotalVo.setSumAllAmountZTotal(sumAllAmountZTotalStr);
        exportTotalVo.setCountOver10BillionZTotal(countOver10BillionZTotal);
        exportTotalVo.setSumOver10BillionZTotal(sumOver10BillionZTotalStr);

        exportTotalVo.setCountRectificationMTotal(countRectificationMTotal);
        exportTotalVo.setSumAllAmountMTotal(sumAllAmountMTotalStr);
        exportTotalVo.setCountOver10BillionMTotal(countOver10BillionMTotal);
        exportTotalVo.setSumOver10BillionMTotal(sumOver10BillionMTotalStr);

        // 6. 导出Excel
        EasyExcelUtil.initResponse(response, "平台项目统计表(项目类)");
        try (
                OutputStream out = response.getOutputStream();
                InputStream templateFile = new ClassPathResource(countAmountTemplatePath).getInputStream();
                com.alibaba.excel.ExcelWriter writer = EasyExcel.write(out).withTemplate(templateFile).build()
        ) {
            WriteSheet sheet = EasyExcel.writerSheet().build();
            // 填充合计数据
            writer.fill(exportTotalVo, sheet);

            // 填充明细数据（包含占比）
            FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();
            writer.fill(countAmountList, fillConfig, sheet);
        } catch (IOException e) {
            throw new RuntimeException("导出平台项目统计表失败：" + e.getMessage(), e);
        }
    }

    @Value("${excel.template.count-light:template/countAmountInvestment.xlsx}")
    private String countAmountInvestmentTemplatePath;

    @Override
    public void exportCountAmountInvestment(DeadlineNodeOutput req, HttpServletResponse response,
                                            JSONObject reqJson) {
        String projectStatus = reqJson.getString("projectStatus");
        // 1. 获取数据（添加非空校验）
        Answer<Object> objectAnswer = this.countAmountInvestment(projectStatus);
        Assert.notNull(objectAnswer, "统计数据查询结果为空");
        Assert.isTrue(objectAnswer.getResult() instanceof List, "统计结果格式错误，应为列表类型");

        List<CountAmountInvestment> countAmountInvestmentList = (List<CountAmountInvestment>) objectAnswer.getResult();

        NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US);

        // 初始化累加变量（按W/D类型区分）
        BigDecimal countRectificationWTotal = BigDecimal.ZERO;
        BigDecimal sumAllAmountWTotal = BigDecimal.ZERO;
        BigDecimal countOver10BillionWTotal = BigDecimal.ZERO;
        BigDecimal sumOver10BillionWTotal = BigDecimal.ZERO;

        BigDecimal countRectificationDTotal = BigDecimal.ZERO;
        BigDecimal sumAllAmountDTotal = BigDecimal.ZERO;
        BigDecimal countOver10BillionDTotal = BigDecimal.ZERO;
        BigDecimal sumOver10BillionDTotal = BigDecimal.ZERO;

        // 循环累加各类型数据
        for (CountAmountInvestment countAmountInvestment : countAmountInvestmentList) {
            if (countAmountInvestment == null) {
                continue;
            }

            // ---------------------- 招商签约未开工项目（W类型）累加 ----------------------
            Long countRectificationW = countAmountInvestment.getCountRectificationW();
            countRectificationWTotal = countRectificationWTotal.add(
                    ObjectUtils.isEmpty(countRectificationW) ? BigDecimal.ZERO : new BigDecimal(countRectificationW)
            );

            String sumAllAmountW = countAmountInvestment.getSumAllAmountW();
            sumAllAmountWTotal = sumAllAmountWTotal.add(BigDecimalUtils.convertStringToBigDecimal(sumAllAmountW, numberFormat));

            Long countOver10BillionW = countAmountInvestment.getCountOver10BillionW();
            countOver10BillionWTotal = countOver10BillionWTotal.add(
                    ObjectUtils.isEmpty(countOver10BillionW) ? BigDecimal.ZERO : new BigDecimal(countOver10BillionW)
            );

            String sumOver10BillionW = countAmountInvestment.getSumOver10BillionW();
            sumOver10BillionWTotal = sumOver10BillionWTotal.add(BigDecimalUtils.convertStringToBigDecimal(sumOver10BillionW, numberFormat));

            // ---------------------- 招商重点调度在谈项目（D类型）累加 ----------------------
            Long countRectificationD = countAmountInvestment.getCountRectificationD();
            countRectificationDTotal = countRectificationDTotal.add(
                    ObjectUtils.isEmpty(countRectificationD) ? BigDecimal.ZERO : new BigDecimal(countRectificationD)
            );

            String sumAllAmountD = countAmountInvestment.getSumAllAmountD();
            sumAllAmountDTotal = sumAllAmountDTotal.add(BigDecimalUtils.convertStringToBigDecimal(sumAllAmountD, numberFormat));

            Long countOver10BillionD = countAmountInvestment.getCountOver10BillionD();
            countOver10BillionDTotal = countOver10BillionDTotal.add(
                    ObjectUtils.isEmpty(countOver10BillionD) ? BigDecimal.ZERO : new BigDecimal(countOver10BillionD)
            );

            String sumOver10BillionD = countAmountInvestment.getSumOver10BillionD();
            sumOver10BillionDTotal = sumOver10BillionDTotal.add(BigDecimalUtils.convertStringToBigDecimal(sumOver10BillionD, numberFormat));
        }

            // ---------------------- 结果转换 + VO赋值（适配Double类型） ----------------------
        ExportCountAmountInvestmentTotalVo exportVo = new ExportCountAmountInvestmentTotalVo();

        // 1. W类型（招商签约未开工）赋值
        exportVo.setCountRectificationWTotal(countRectificationWTotal.longValue());
        // BigDecimal转Double（保留两位小数精度）
        exportVo.setSumAllAmountWTotal(sumAllAmountWTotal.setScale(2, RoundingMode.HALF_UP).doubleValue());
        exportVo.setCountOver10BillionWTotal(countOver10BillionWTotal.longValue());
        exportVo.setSumOver10BillionWTotal(sumOver10BillionWTotal.setScale(2, RoundingMode.HALF_UP).doubleValue());

        // 2. D类型（招商重点调度在谈）赋值
        exportVo.setCountRectificationDTotal(countRectificationDTotal.longValue());
        exportVo.setSumAllAmountDTotal(sumAllAmountDTotal.setScale(2, RoundingMode.HALF_UP).doubleValue());
        exportVo.setCountOver10BillionDTotal(countOver10BillionDTotal.longValue());
        exportVo.setSumOver10BillionDTotal(sumOver10BillionDTotal.setScale(2, RoundingMode.HALF_UP).doubleValue());

        Assert.notEmpty(countAmountInvestmentList, "统计数据列表为空");

        // 2. 初始化响应（工具类处理文件名和响应头）
        EasyExcelUtil.initResponse(response, "平台项目统计表(项目类)");
        // 3.  try-with-resources 自动关闭资源
        try (
                OutputStream out = response.getOutputStream();  // 响应输出流
                InputStream templateFile = new ClassPathResource(countAmountInvestmentTemplatePath).getInputStream();  // 模板输入流
                com.alibaba.excel.ExcelWriter writer = EasyExcel.write(out).withTemplate(templateFile).build()  // Excel写入器
        ) {
            // 4. 创建工作表（默认第一个Sheet）
            WriteSheet sheet = EasyExcel.writerSheet().build();

            writer.fill(exportVo, sheet);

            // 5. 配置填充规则（强制换行，避免数据覆盖）
            FillConfig fillConfig = FillConfig.builder()
                    .forceNewRow(true)  // 列表数据每条占一行，适合表格型模板
                    .build();

            // 6. 填充数据（模板中需有 {.字段名} 占位符，与CountAmount类字段对应）
            writer.fill(countAmountInvestmentList, fillConfig, sheet);
        } catch (IOException e) {
            // 记录异常日志（建议使用日志框架如SLF4J）
            throw new RuntimeException("导出平台项目统计表失败：" + e.getMessage(), e);
        }
    }

    @Value("${excel.template.count-light:template/CountAmountBefore.xlsx}")
    private String CountAmountBeforeTemplatePath;

    @Override
    public void exportCountAmountBefore(DeadlineNodeOutput req, HttpServletResponse response,JSONObject reqJson) {
        String startTime = reqJson.getString("startTime");
        String endTime = reqJson.getString("endTime");
        String projectChildCategoryType = reqJson.getString("projectChildCategoryType");

        // 1. 获取数据（添加非空校验）
        Answer<Object> objectAnswer = this.countAmountBefore(reqJson);
        Assert.notNull(objectAnswer, "统计数据查询结果为空");
        Assert.isTrue(objectAnswer.getResult() instanceof List, "统计结果格式错误，应为列表类型");

        List<CountAmountBefore> countAmountBeforeList = (List<CountAmountBefore>) objectAnswer.getResult();

        ExportCountAmountBeforeTotalVo exportCountAmountBeforeTotalVo = new ExportCountAmountBeforeTotalVo();

        String startYear = startTime.substring(0, 4);
        String startMonth = startTime.substring(5, 7);
        String startDate= startTime.substring(8, 10);
        String endYear = endTime.substring(0, 4);
        String endMonth = endTime.substring(5, 7);
        String endDate= endTime.substring(8, 10);
        startTime = startYear +"年"+startMonth+"月"+startDate+"日";
        endTime = endYear +"年"+endMonth+"月"+endDate+"日";
        exportCountAmountBeforeTotalVo.setStartTime(startTime);
        exportCountAmountBeforeTotalVo.setEndTime(endTime);
        exportCountAmountBeforeTotalVo.setProjectChildCategoryType(projectChildCategoryType);

        BigDecimal countRectificationTotal = BigDecimal.ZERO;
        BigDecimal sumAllAmountTotal = BigDecimal.ZERO;
        BigDecimal countCancelledTotal = BigDecimal.ZERO;
        BigDecimal sumCancelledInvestmentTotal = BigDecimal.ZERO;
        BigDecimal countRectificationBeforeTotal = BigDecimal.ZERO;
        BigDecimal sumAmountBeforeTotal = BigDecimal.ZERO;

        NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US);

        for (CountAmountBefore countAmountBefore : countAmountBeforeList) {
            if (countAmountBefore == null) {
                continue;
            }

            Long countRectification = countAmountBefore.getCountRectification();
            countRectificationTotal = countRectificationTotal.add(
                    ObjectUtils.isEmpty(countRectification) ? BigDecimal.ZERO : new BigDecimal(countRectification)
            );
            Long countCancelled = countAmountBefore.getCountCancelled();
            countCancelledTotal = countCancelledTotal.add(
                    ObjectUtils.isEmpty(countCancelled) ? BigDecimal.ZERO : new BigDecimal(countCancelled)
            );

            String sumAllAmountStr = countAmountBefore.getSumAllAmount();
            sumAllAmountTotal = sumAllAmountTotal.add(BigDecimalUtils.convertStringToBigDecimal(sumAllAmountStr, numberFormat));

            String sumCancelledInvestment = countAmountBefore.getSumCancelledInvestment();
            sumCancelledInvestmentTotal = sumCancelledInvestmentTotal.add(BigDecimalUtils.convertStringToBigDecimal(sumCancelledInvestment, numberFormat));

            Long countRectificationBefore = countAmountBefore.getCountRectificationBefore();
            countRectificationBeforeTotal = countRectificationBeforeTotal.add(
                    ObjectUtils.isEmpty(countRectificationBefore) ? BigDecimal.ZERO : new BigDecimal(countRectificationBefore)
            );

            String sumAmountBeforeStr = countAmountBefore.getSumAmountBefore();
            sumAmountBeforeTotal = sumAmountBeforeTotal.add(BigDecimalUtils.convertStringToBigDecimal(sumAmountBeforeStr, numberFormat));
        }

        String sumAllAmountTotalStr = BigDecimalUtils.formatBigDecimalToString(sumAllAmountTotal, true);
        String sumAmountBeforeTotalStr = BigDecimalUtils.formatBigDecimalToString(sumAmountBeforeTotal, true);
        String sumCancelledInvestmentTotalStr = BigDecimalUtils.formatBigDecimalToString(sumCancelledInvestmentTotal, true);
        Long countRectificationTotalLong = countRectificationTotal.longValue();
        long countCancelledTotalLong = countCancelledTotal.longValue();
        Long countRectificationBeforeTotalLong = countRectificationBeforeTotal.longValue();

        exportCountAmountBeforeTotalVo.setCountRectificationTotal(countRectificationTotalLong);
        exportCountAmountBeforeTotalVo.setSumAllAmountTotal(sumAllAmountTotalStr);

        exportCountAmountBeforeTotalVo.setCountCancelledTotal(countCancelledTotalLong);
        exportCountAmountBeforeTotalVo.setSumCancelledInvestmentTotal(sumCancelledInvestmentTotalStr);

        exportCountAmountBeforeTotalVo.setCountRectificationBeforeTotal(countRectificationBeforeTotalLong);
        exportCountAmountBeforeTotalVo.setSumAmountBeforeTotal(sumAmountBeforeTotalStr);

        // 处理 countBeforeRatioTotal（long 计算 → double 百分比）
        if ("0".equals(countRectificationTotal)) {
            exportCountAmountBeforeTotalVo.setCountBeforeRatioTotal("0.0");
        } else {
            // 2. 计算：(分子/分母)*100 → 保留两位小数（四舍五入）
            BigDecimal ratioPercent = countRectificationBeforeTotal.divide(countRectificationTotal, 4, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal("100"))  // 转百分比数值
                    .setScale(1, RoundingMode.HALF_UP);
            // 3. 转为 double 赋值（结果如 33.33）
            exportCountAmountBeforeTotalVo.setCountBeforeRatioTotal(ratioPercent.toString());
        }

        if ("0.0".equals(sumAllAmountTotal)) {
            exportCountAmountBeforeTotalVo.setAmountBeforeRatioTotal("0.0");
        } else {
            // 2. 计算：(分子/分母)*100 → 保留两位小数（四舍五入）
            BigDecimal ratioPercent = sumAmountBeforeTotal.divide(sumAllAmountTotal, 4, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal("100"))  // 转百分比数值
                    .setScale(1, RoundingMode.HALF_UP);
            // 3. 转为 double 赋值（结果如 56.78）
            exportCountAmountBeforeTotalVo.setAmountBeforeRatioTotal(ratioPercent.toString());
        }



        Assert.notEmpty(countAmountBeforeList, "统计数据列表为空");

        // 2. 初始化响应（工具类处理文件名和响应头）
        EasyExcelUtil.initResponse(response, "平台项目统计表(项目类)");
        // 3.  try-with-resources 自动关闭资源
        try (
                OutputStream out = response.getOutputStream();  // 响应输出流
                InputStream templateFile = new ClassPathResource(CountAmountBeforeTemplatePath).getInputStream();  // 模板输入流
                com.alibaba.excel.ExcelWriter writer = EasyExcel.write(out).withTemplate(templateFile).build()  // Excel写入器
        ) {
            // 4. 创建工作表（默认第一个Sheet）
            WriteSheet sheet = EasyExcel.writerSheet().build();

            writer.fill(exportCountAmountBeforeTotalVo, sheet);
            // 5. 配置填充规则（强制换行，避免数据覆盖）
            FillConfig fillConfig = FillConfig.builder()
                    .forceNewRow(true)  // 列表数据每条占一行，适合表格型模板
                    .build();

            // 6. 填充数据（模板中需有 {.字段名} 占位符，与CountAmount类字段对应）
            writer.fill(countAmountBeforeList, fillConfig, sheet);
        } catch (IOException e) {
            // 记录异常日志（建议使用日志框架如SLF4J）
            throw new RuntimeException("导出平台项目统计表失败：" + e.getMessage(), e);
        }


    }

    @Override
    public Answer<Object> countAmountDetails(JSONObject reqJson) {
        String startTime = reqJson.getString("startTime");
        String label = reqJson.getString("label");
        String projectChildCategoryType = reqJson.getString("projectChildCategoryType");
        String departmentName = reqJson.getString("departmentName");
        Integer isOver10Billion = reqJson.getInteger("isOver10Billion");
        String projectStatus = reqJson.getString("projectStatus");
        List<RectificationInfo> rectificationInfoList = rectificationInfoMapper.countAmountDetails(startTime, projectChildCategoryType, label, departmentName, isOver10Billion,projectStatus);
        return Answer.ok(rectificationInfoList);
    }

    @Override
    public Answer<Object> rectificationDetails(JSONObject reqJson) {
        String deadline = reqJson.getString("deadline");
        String projectChildCategoryType = reqJson.getString("projectChildCategoryType");
        String projectStatus = reqJson.getString("projectStatus");
        List<RectificationInfo> rectificationInfoList = rectificationInfoMapper.rectificationDetails(deadline, projectChildCategoryType,projectStatus);
        return Answer.ok(rectificationInfoList);
    }

    @Override
    public void exportRectificationDetails(DeadlineNodeOutput req, HttpServletResponse response,JSONObject reqJson) {
        String deadline = reqJson.getString("deadline");
        String projectChildCategoryType = reqJson.getString("projectChildCategoryType");
        Answer<Object> objectAnswer = this.rectificationDetails(reqJson);

        if (objectAnswer == null) {
            throw new RuntimeException("分页查询接口返回空，请检查接口实现");
        }
        if (objectAnswer.getCode() != 200) {
            throw new RuntimeException(String.format("分页查询失败：状态码=%d，提示=%s",
                    objectAnswer.getCode(), objectAnswer.getMsg()));
        }
        List<RectificationInfo> rectificationInfoList = (List<RectificationInfo>) objectAnswer.getResult();

        List<Map<String, Object>> rows = new ArrayList<>();
        if(projectChildCategoryType.contains("在建")){
             rows = rectificationInfoList.stream().map(n -> {
                Map<String, Object> m = new LinkedHashMap<>();
                m.put("项目名称", n.getTitle());
                m.put("投资额", n.getAmount());
                m.put("牵头单位", n.getDepartmentName());
                m.put("牵头领导", n.getLeadLeader());
                m.put("责任人", n.getPersonLiaible());
                Date originalDeadline = n.getDeadline(); // 假设getDeadline()返回Date类型
                m.put("要求完成时间", originalDeadline != null ? sdf.format(originalDeadline) : null);
                Date endTime = n.getEndTime(); // 假设返回Date类型
                m.put("实际完成时间", endTime != null ? sdf.format(endTime) : null);
                // 计划投资额和实际投资额（String类型）
                String amountPlanStr = n.getAmountPlan();
                String amountActualStr = n.getAmount();
                m.put("计划投资额", amountActualStr);
                m.put("实际投资额", amountPlanStr);

                // 计算投资比例：实际投资 / 计划投资（处理异常情况）
                m.put("投资比例", calculateInvestmentRatio(amountActualStr, amountPlanStr));
                return m;
            }).collect(Collectors.toList());
        }else {
            rows = rectificationInfoList.stream().map(n -> {
                Map<String, Object> m = new LinkedHashMap<>();
                m.put("项目名称", n.getTitle());
                m.put("投资额", n.getAmount());
                m.put("牵头单位", n.getDepartmentName());
                m.put("牵头领导", n.getLeadLeader());
                m.put("责任人", n.getPersonLiaible());
                Date originalDeadline = n.getDeadline(); // 假设getDeadline()返回Date类型
                m.put("要求完成时间", originalDeadline != null ? sdf.format(originalDeadline) : null);
                Date endTime = n.getEndTime(); // 假设返回Date类型
                m.put("实际完成时间", endTime != null ? sdf.format(endTime) : null);
                return m;
            }).collect(Collectors.toList());
        }

        String fileName = "项目列表导出" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + ".xlsx";
        ExcelWriter writer = ExcelUtil.getBigWriter();
        try {
            LinkedHashMap<String, String> header = new LinkedHashMap<>();
            if(projectChildCategoryType.contains("在建")) {
                header.put("项目名称", "项目名称");
                header.put("计划投资额", "计划投资额");
                header.put("实际投资额", "实际投资额");
                header.put("投资比例", "投资比例");
                header.put("牵头单位", "牵头单位");
                header.put("牵头领导", "牵头领导");
                header.put("责任人", "责任人");
                header.put("要求完成时间", "要求完成时间");
                header.put("实际完成时间", "实际完成时间");
            }else {
                header.put("项目名称", "项目名称");
                header.put("投资额", "投资额");
                header.put("牵头单位", "牵头单位");
                header.put("牵头领导", "牵头领导");
                header.put("责任人", "责任人");
                header.put("要求完成时间", "要求完成时间");
                header.put("实际完成时间", "实际完成时间");
            }

            header.forEach(writer::addHeaderAlias);
            writer.setOnlyAlias(true);

            String title = deadline + "月底前" + projectChildCategoryType + "详情表";
            writer.merge(header.size() - 1, title);

            Workbook wb = writer.getWorkbook();
            Sheet sheet = writer.getSheet();
            Row r0 = sheet.getRow(0) != null ? sheet.getRow(0) : sheet.createRow(0);
            Cell c0 = r0.getCell(0) != null ? r0.getCell(0) : r0.createCell(0);
            CellStyle titleStyle = wb.createCellStyle();
            titleStyle.setAlignment(org.apache.poi.ss.usermodel.HorizontalAlignment.CENTER);
            titleStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
            org.apache.poi.ss.usermodel.Font titleFont = wb.createFont();
            titleFont.setBold(true);
            titleFont.setFontHeightInPoints((short) 12);
            titleStyle.setFont(titleFont);
            c0.setCellStyle(titleStyle);
            r0.setHeightInPoints(22);

            sheet.createFreezePane(0, 2);

            // 创建自动换行的单元格样式
            CellStyle wrapStyle = wb.createCellStyle();
            wrapStyle.setWrapText(true);


            writer.write(rows, true);

            int i = 0;
            for (String ignored : header.keySet()) {
                writer.setColumnWidth(i, 20);
                // 将自动换行样式应用于第二列
                if (i == 1) {
                    sheet.setDefaultColumnStyle(i, wrapStyle);
                }
                i++;
            }

            // 输出
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + URLEncoder.encode(fileName, java.nio.charset.StandardCharsets.UTF_8.toString()));
            ServletOutputStream out = response.getOutputStream();
            writer.flush(out, true);
            out.flush();
        } catch (Exception e) {
            throw new RuntimeException("导出失败", e);
        } finally {
            writer.close();
        }
    }

    private static String calculateInvestmentRatio(String amountPlanStr, String amountActualStr) {
        // 1. 校验空值/空字符串
        if (StringUtils.isBlank(amountPlanStr) || StringUtils.isBlank(amountActualStr)) {
            return "-";
        }

        try {
            // 2. 转换为BigDecimal（支持整数、小数格式，去除首尾空格）
            BigDecimal amountPlan = new BigDecimal(amountPlanStr.trim());
            BigDecimal amountActual = new BigDecimal(amountActualStr.trim());

            // 3. 处理计划投资为0的情况（避免除零异常）
            if (BigDecimal.ZERO.compareTo(amountPlan) == 0) {
                return "-";
            }

            // 4. 计算比例：实际投资 / 计划投资
            // 先除法（保留足够精度，避免中间误差），再强制保留2位小数（不足补0，四舍五入）
            BigDecimal ratio = amountActual.divide(amountPlan, 10, RoundingMode.HALF_UP) // 中间保留10位精度
                    .setScale(2, RoundingMode.HALF_UP); // 最终强制2位小数，补0

            // 5. 可选：转换为百分比格式（同样补0，如 1.23 → 123.00%）
            // BigDecimal percentRatio = ratio.multiply(new BigDecimal(100))
            //                                .setScale(2, RoundingMode.HALF_UP);
            // return percentRatio.toString() + "%";

            // 直接返回小数格式（如：1.20、0.75、2.00）
            return ratio.toString();

        } catch (NumberFormatException e) {
            // 6. 处理非数字格式的异常（如字符串包含字母、特殊字符）
            return "-";
        }
    }

    @Override
    public Answer<Object> deadlineRed() {
        List<DeadlineNode> deadlineNodeList = rectificationInfoMapper.allDeadline();

        // 按 "name+department" 联合分组：key为"name|department"，value为统计信息
        Map<String, DeadlineRedVo> nameDeptStatMap = new HashMap<>();

        for (DeadlineNode node : deadlineNodeList) {
            // 处理部门名称（标准化）
            String deptName = node.getDepartmentName();
            if (StringUtils.isBlank(deptName)) {
                continue; // 跳过部门为空的记录
            }
            if (deptName.contains("发改")) {
                deptName = "市发改委";
            } else if (deptName.contains("资建")) {
                deptName = "市资建局";
            } else if (deptName.contains("林业")) {
                deptName = "市林业局";
            } else if (deptName.contains("财政")) {
                deptName = "市财政局";
            } else if (deptName.contains("生态")) {
                deptName = "市生态环境局";
            } else if (deptName.contains("应急")) {
                deptName = "市应急管理局";
            } else if (deptName.contains("自规部门")) {
                deptName = "市自然资源规划局";
            } else if (deptName.contains("交通")) {
                deptName = "市交通运输局";
            } else if (deptName.contains("住建")) {
                deptName = "市住新局"; // 建议确认名称规范性
            }

            if(!DEPARTMENTS.contains(deptName.trim())){
                continue;
            }

            // 获取当前记录的name（name为空则跳过）
            String name = node.getName();
            if (StringUtils.isBlank(name)) {
                continue;
            }

            // 生成联合主键：用"|"分隔name和department，确保唯一
            String key = name + "|" + deptName;
            String warnLevel = node.getWarnLevel();

            // 初始化统计对象（若不存在）
            nameDeptStatMap.putIfAbsent(key, new DeadlineRedVo());
            DeadlineRedVo stat = nameDeptStatMap.get(key);
            stat.setName(name);
            stat.setDeptName(deptName);

            // 累计总条数
            stat.setTotalCount(stat.getTotalCount() + 1);

            // 统计各灯状态数量
            if (StringUtils.isNotBlank(warnLevel)) {
                switch (warnLevel.trim()) {
                    case "0":
                        stat.setGreenCount(stat.getGreenCount() + 1);
                        break;
                    case "1":
                        stat.setYellowCount(stat.getYellowCount() + 1);
                        break;
                    case "2":
                        stat.setRedCount(stat.getRedCount() + 1);
                        break;
                }
            }
        }

        // 过滤仅保留红灯有数值的记录（redCount > 0）并计算红灯率（转为String）
        List<DeadlineRedVo> resultList = new ArrayList<>();
        // 定义格式化器：保留2位小数，确保格式统一（如"33.33%"）
        DecimalFormat df = new DecimalFormat("0.0%");
        for (DeadlineRedVo stat : nameDeptStatMap.values()) {
            if (stat.getRedCount() <= 0) {
                continue; // 排除红灯无数据的记录
            }

            // 计算红灯率并格式化为String
            int total = stat.getTotalCount();
            int red = stat.getRedCount();
            if (total > 0) {
                BigDecimal rate = new BigDecimal(red)
                        .divide(new BigDecimal(total), 4, RoundingMode.HALF_UP);
                // 格式化：小数转百分比字符串（保留2位小数）
                stat.setRedRate(df.format(rate));
            } else {
                // 总数为0时，红灯率设为"0.00%"
                stat.setRedRate("0.0%");
            }

            resultList.add(stat);
        }
        // 按部门、名称排序
        resultList.sort(Comparator.comparing(DeadlineRedVo::getDeptName)
                .thenComparing(DeadlineRedVo::getName));

        return Answer.ok(resultList);
    }


    @Value("${excel.template.count-light:template/deadlineRed.xlsx}")
    private String deadlineRedTemplatePath;
    @Override
    public void exportDeadlineRed(DeadlineNodeOutput req, HttpServletResponse response) {

        // 1. 获取数据（添加非空校验）
        Answer<Object> objectAnswer = this.deadlineRed();
        Assert.notNull(objectAnswer, "统计数据查询结果为空");
        Assert.isTrue(objectAnswer.getResult() instanceof List, "统计结果格式错误，应为列表类型");

        List<DeadlineRedVo> resultList = (List<DeadlineRedVo>) objectAnswer.getResult();

        Assert.notEmpty(resultList, "统计数据列表为空");

        // 2. 初始化响应（工具类处理文件名和响应头）
        EasyExcelUtil.initResponse(response, "平台项目统计表(项目类)");
        // 3.  try-with-resources 自动关闭资源
        try (
                OutputStream out = response.getOutputStream();  // 响应输出流
                InputStream templateFile = new ClassPathResource(deadlineRedTemplatePath).getInputStream();  // 模板输入流
                com.alibaba.excel.ExcelWriter writer = EasyExcel.write(out).withTemplate(templateFile).build()  // Excel写入器
        ) {
            // 4. 创建工作表（默认第一个Sheet）
            WriteSheet sheet = EasyExcel.writerSheet().build();

            // 5. 配置填充规则（强制换行，避免数据覆盖）
            FillConfig fillConfig = FillConfig.builder()
                    .forceNewRow(true)  // 列表数据每条占一行，适合表格型模板
                    .build();

            // 6. 填充数据（模板中需有 {.字段名} 占位符，与CountAmount类字段对应）
            writer.fill(resultList, fillConfig, sheet);
        } catch (IOException e) {
            // 记录异常日志（建议使用日志框架如SLF4J）
            throw new RuntimeException("导出平台项目统计表失败：" + e.getMessage(), e);
        }


    }

    @Override
    public Answer<Object> countRedLight() {
        // 1. 简化类型列表初始化（Java 8 及以下兼容）
        List<Integer> typeList = new ArrayList<>();
        typeList.add(24);
        typeList.add(85);
        typeList.add(86);
        // 或用 Arrays.asList(24,85,86)（返回固定大小列表，不影响后续查询）

        // 2. 查询符合条件的提醒信息
        List<ReminderInfo> reminderInfos = reminderInfoMapper.selectList(
                new LambdaQueryWrapper<ReminderInfo>().in(ReminderInfo::getType, typeList)
        );

        // 3. DeadlineNode 转 Map（id -> 节点，处理重复id）
        Map<Object, DeadlineNode> deadlineNodeMap = deadlineNodeMapper.selectList(new LambdaQueryWrapper<>())
                .stream()
                .collect(Collectors.toMap(
                        DeadlineNode::getId,
                        Function.identity(),
                        (oldNode, newNode) -> oldNode, // 重复id保留第一个，避免报错
                        LinkedHashMap::new
                ));

        // 4. 按 deadlineId 去重（过滤 null 的 deadlineId，避免无效查询）
        Map<Object, ReminderInfo> uniqueReminderMap = reminderInfos.stream()
                .filter(reminderInfo -> reminderInfo.getDeadlineId() != null) // 过滤 null 值
                .collect(Collectors.toMap(
                        ReminderInfo::getDeadlineId,
                        Function.identity(),
                        (oldInfo, newInfo) -> newInfo // 重复deadlineId保留最后一个，需改第一个则换 oldInfo
                ));

        // 5. 类型-红灯次数映射（Java 8 兼容：用 HashMap 替代 Map.of()）
        Map<Integer, Integer> typeRedCountMap = new HashMap<>();
        typeRedCountMap.put(24, 1);
        typeRedCountMap.put(85, 2);
        typeRedCountMap.put(86, 3);

        LambdaQueryWrapper<RectificationInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.ne(RectificationInfo::getState, "已关闭");

        Map<Object, RectificationInfo> rectificationMap =
                rectificationInfoMapper.selectList(wrapper)
                .stream()
                .collect(Collectors.toMap(
                        RectificationInfo::getId,
                        Function.identity(),
                        (oldNode, newNode) -> oldNode, // 重复id保留第一个，避免报错
                        LinkedHashMap::new
                ));



        // 6. 封装结果：获取不到 DeadlineNode 则直接跳过
        List<CountRedLightVo> countRedLightVos = uniqueReminderMap.values().stream()
                .map(reminderInfo -> {
                    // 根据 deadlineId 获取节点，无则返回 null（后续过滤）
                    DeadlineNode deadlineNode = deadlineNodeMap.get(reminderInfo.getDeadlineId());
                    if (deadlineNode == null) {
                        return null; // 标记为跳过
                    }
//                    deadlineNode.getRectificationId();
                    RectificationInfo rectificationInfo = rectificationMap.get(deadlineNode.getRectificationId());
                    // 复制属性并设置红灯次数
                    CountRedLightVo vo = new CountRedLightVo();
                    if (deadlineNode.getProgress() != null &&
                            deadlineNode.getProgress().equals("/")){
                        deadlineNode.setProgress("无此环节");
                    } else if(deadlineNode.getEndTime()!= null){
                        deadlineNode.setProgress("已完成");
                    }else{
                        deadlineNode.setProgress("未完成");
                    }
                    BeanUtils.copyProperties(deadlineNode, vo);
                    // 给他设置节点关联的项目的title;
                    if (rectificationInfo!=null){
                        vo.setRectificationLinkId(rectificationInfo.getId());
                        vo.setTitle(rectificationInfo.getTitle());
                        vo.setProjectType(rectificationInfo.getProjectType());
                        vo.setProjectChildType(rectificationInfo.getProjectChildType());
                        vo.setDepartment(rectificationInfo.getDepartmentName());
                        vo.setProjectChildCategoryType(rectificationInfo.getProjectChildCategoryType());
                        vo.setStatus(rectificationInfo.getState());
                        if (StrUtil.isEmptyIfStr(deadlineNode.getDepartmentName())){
                            vo.setDepartmentName(rectificationInfo.getDepartmentName());
                        }
                    }
                    vo.setDeadline(DateUtil.format(deadlineNode.getDeadline(),"yyyy-MM-dd"));
                    vo.setRedCount(typeRedCountMap.getOrDefault(reminderInfo.getType(), 0));
                    return vo;
                })
                .filter(Objects::nonNull) // 过滤掉无对应 DeadlineNode 的数据
                .collect(Collectors.toList());
        countRedLightVos = countRedLightVos.stream()
                .filter(item -> item.getRectificationLinkId() != null) // 假设通过getter获取该字段
                .collect(Collectors.toList());
        return Answer.ok(countRedLightVos);
    }

    @Value("${excel.template.count-light:template/countRedLightPath.xlsx}")
    private String countRedLightPath;

    @Override
    public void exportCountRedLight(DeadlineNodeOutput req, HttpServletResponse response) {

        // 1. 获取数据（添加非空校验）
        Answer<Object> objectAnswer = this.countRedLight();
        Assert.notNull(objectAnswer, "统计数据查询结果为空");
        Assert.isTrue(objectAnswer.getResult() instanceof List, "统计结果格式错误，应为列表类型");

        List<CountRedLightVo> resultList = (List<CountRedLightVo>) objectAnswer.getResult();

        Assert.notEmpty(resultList, "统计数据列表为空");

        // 2. 初始化响应（工具类处理文件名和响应头）
        EasyExcelUtil.initResponse(response, "红灯环节次数统计表");
        // 3.  try-with-resources 自动关闭资源
        try (
                OutputStream out = response.getOutputStream();  // 响应输出流
                InputStream templateFile = new ClassPathResource(countRedLightPath).getInputStream();  // 模板输入流
                com.alibaba.excel.ExcelWriter writer = EasyExcel.write(out).withTemplate(templateFile).build()  // Excel写入器
        ) {
            // 4. 创建工作表（默认第一个Sheet）
            WriteSheet sheet = EasyExcel.writerSheet().build();

            // 5. 配置填充规则（强制换行，避免数据覆盖）
            FillConfig fillConfig = FillConfig.builder()
                    .forceNewRow(true)  // 列表数据每条占一行，适合表格型模板
                    .build();

            // 6. 填充数据（模板中需有 {.字段名} 占位符，与CountAmount类字段对应）
            writer.fill(resultList, fillConfig, sheet);
        } catch (IOException e) {
            // 记录异常日志（建议使用日志框架如SLF4J）
            throw new RuntimeException("导出平台项目统计表失败：" + e.getMessage(), e);
        }


    }

    @Override
    public Answer<Object> terminationRectification(JSONObject reqJson) {
        String projectChildCategoryType = reqJson.getString("projectChildCategoryType");

        List<TerminationRectificationVo> terminationRectificationVoList = rectificationInfoMapper.terminationRectification(projectChildCategoryType);
        if (terminationRectificationVoList == null) {
            terminationRectificationVoList = new ArrayList<>();
        }

        NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US);

        BigDecimal totalCountSum = BigDecimal.ZERO;
        BigDecimal cancelledProjectCountSum = BigDecimal.ZERO;
        BigDecimal totalAmountSum = BigDecimal.ZERO;
        BigDecimal cancelledTotalAmountSum = BigDecimal.ZERO;
        BigDecimal over10ProjectCountSum = BigDecimal.ZERO;
        BigDecimal cancelledOver10ProjectCountSum = BigDecimal.ZERO;
        BigDecimal over10TotalAmountSum = BigDecimal.ZERO;
        BigDecimal cancelledOver10TotalAmountSum = BigDecimal.ZERO;

        for (TerminationRectificationVo vo : terminationRectificationVoList) {
            BigDecimal totalCount = BigDecimalUtils.convertStringToBigDecimal(vo.getTotalCount(), numberFormat);
            BigDecimal cancelledProjectCount = BigDecimalUtils.convertStringToBigDecimal(vo.getCancelledProjectCount(), numberFormat);
            BigDecimal totalAmount = BigDecimalUtils.convertStringToBigDecimal(vo.getTotalAmount(), numberFormat);
            BigDecimal cancelledTotalAmount = BigDecimalUtils.convertStringToBigDecimal(vo.getCancelledTotalAmount(), numberFormat);
            BigDecimal over10ProjectCount = BigDecimalUtils.convertStringToBigDecimal(vo.getOver10ProjectCount(), numberFormat);
            BigDecimal cancelledOver10ProjectCount = BigDecimalUtils.convertStringToBigDecimal(vo.getCancelledOver10ProjectCount(), numberFormat);
            BigDecimal over10TotalAmount = BigDecimalUtils.convertStringToBigDecimal(vo.getOver10TotalAmount(), numberFormat);
            BigDecimal cancelledOver10TotalAmount = BigDecimalUtils.convertStringToBigDecimal(vo.getCancelledOver10TotalAmount(), numberFormat);

            totalCountSum = totalCountSum.add(totalCount);
            cancelledProjectCountSum = cancelledProjectCountSum.add(cancelledProjectCount);
            totalAmountSum = totalAmountSum.add(totalAmount);
            cancelledTotalAmountSum = cancelledTotalAmountSum.add(cancelledTotalAmount);
            over10ProjectCountSum = over10ProjectCountSum.add(over10ProjectCount);
            cancelledOver10ProjectCountSum = cancelledOver10ProjectCountSum.add(cancelledOver10ProjectCount);
            over10TotalAmountSum = over10TotalAmountSum.add(over10TotalAmount);
            cancelledOver10TotalAmountSum = cancelledOver10TotalAmountSum.add(cancelledOver10TotalAmount);

            // 关键修改：个数字段使用新方法 formatCountAsInteger（整数格式，无小数点）
            vo.setTotalCount(BigDecimalUtils.formatCountAsInteger(totalCount));
            vo.setCancelledProjectCount(BigDecimalUtils.formatCountAsInteger(cancelledProjectCount));
            vo.setOver10ProjectCount(BigDecimalUtils.formatCountAsInteger(over10ProjectCount));
            vo.setCancelledOver10ProjectCount(BigDecimalUtils.formatCountAsInteger(cancelledOver10ProjectCount));

            // 金额字段保持不变（使用原有 formatAmount 方法，两位小数）
            vo.setTotalAmount(BigDecimalUtils.formatAmount(totalAmount, false));
            vo.setCancelledTotalAmount(BigDecimalUtils.formatAmount(cancelledTotalAmount, false));
            vo.setOver10TotalAmount(BigDecimalUtils.formatAmount(over10TotalAmount, false));
            vo.setCancelledOver10TotalAmount(BigDecimalUtils.formatAmount(cancelledOver10TotalAmount, false));
        }

        for (TerminationRectificationVo vo : terminationRectificationVoList) {
            BigDecimal totalCount = BigDecimalUtils.convertStringToBigDecimal(vo.getTotalCount(), numberFormat);
            BigDecimal cancelledProjectCount = BigDecimalUtils.convertStringToBigDecimal(vo.getCancelledProjectCount(), numberFormat);
            BigDecimal totalAmount = BigDecimalUtils.convertStringToBigDecimal(vo.getTotalAmount(), numberFormat);
            BigDecimal cancelledTotalAmount = BigDecimalUtils.convertStringToBigDecimal(vo.getCancelledTotalAmount(), numberFormat);
            BigDecimal over10ProjectCount = BigDecimalUtils.convertStringToBigDecimal(vo.getOver10ProjectCount(), numberFormat);
            BigDecimal cancelledOver10ProjectCount = BigDecimalUtils.convertStringToBigDecimal(vo.getCancelledOver10ProjectCount(), numberFormat);
            BigDecimal over10TotalAmount = BigDecimalUtils.convertStringToBigDecimal(vo.getOver10TotalAmount(), numberFormat);
            BigDecimal cancelledOver10TotalAmount = BigDecimalUtils.convertStringToBigDecimal(vo.getCancelledOver10TotalAmount(), numberFormat);

            // 占比字段保持不变（使用原有 calculateProportion 方法，一位小数）
            vo.setTotalCountProportion(BigDecimalUtils.calculateProportion(totalCount, totalCountSum));
            vo.setCancelledProjectCountProportion(BigDecimalUtils.calculateProportion(cancelledProjectCount, cancelledProjectCountSum));
            vo.setTotalAmountProportion(BigDecimalUtils.calculateProportion(totalAmount, totalAmountSum));
            vo.setCancelledTotalAmountProportion(BigDecimalUtils.calculateProportion(cancelledTotalAmount, cancelledTotalAmountSum));
            vo.setOver10ProjectCountProportion(BigDecimalUtils.calculateProportion(over10ProjectCount, over10ProjectCountSum));
            vo.setCancelledOver10ProjectCountProportion(BigDecimalUtils.calculateProportion(cancelledOver10ProjectCount, cancelledOver10ProjectCountSum));
            vo.setOver10TotalAmountProportion(BigDecimalUtils.calculateProportion(over10TotalAmount, over10TotalAmountSum));
            vo.setCancelledOver10TotalAmountProportion(BigDecimalUtils.calculateProportion(cancelledOver10TotalAmount, cancelledOver10TotalAmountSum));
        }

        TerminationRectificationTotalVo totalVo = new TerminationRectificationTotalVo();
        // 关键修改：合计中的个数字段也使用 formatCountAsInteger（整数格式）
        totalVo.setTotalCountSum(BigDecimalUtils.formatCountAsInteger(totalCountSum));
        totalVo.setCancelledProjectCountSum(BigDecimalUtils.formatCountAsInteger(cancelledProjectCountSum));
        totalVo.setOver10ProjectCountSum(BigDecimalUtils.formatCountAsInteger(over10ProjectCountSum));
        totalVo.setCancelledOver10ProjectCountSum(BigDecimalUtils.formatCountAsInteger(cancelledOver10ProjectCountSum));

        // 合计中的金额字段保持不变
        totalVo.setTotalAmountSum(BigDecimalUtils.formatAmount(totalAmountSum, false));
        totalVo.setCancelledTotalAmountSum(BigDecimalUtils.formatAmount(cancelledTotalAmountSum, false));
        totalVo.setOver10TotalAmountSum(BigDecimalUtils.formatAmount(over10TotalAmountSum, false));
        totalVo.setCancelledOver10TotalAmountProportionSum(BigDecimalUtils.formatAmount(cancelledOver10TotalAmountSum, false));

        terminationRectificationVoList.sort((data1, data2) -> {
            String name1 = data1.getDepartment();
            String name2 = data2.getDepartment();
            // 获取两个区域在排序列表中的索引
            int index1 = AREA_ORDER.indexOf(name1);
            int index2 = AREA_ORDER.indexOf(name2);
            // 索引小的排前面；未在列表中的区域放最后（按名称自然排序）
            if (index1 == -1 && index2 == -1) {
                return name1.compareTo(name2); // 均不在列表中，按名称排序
            } else if (index1 == -1) {
                return 1; // 前者不在列表中，放后面
            } else if (index2 == -1) {
                return -1; // 后者不在列表中，放后面
            } else {
                return Integer.compare(index1, index2); // 按索引顺序排序
            }
        });

        JSONObject result = new JSONObject();
        result.put("departmentList", terminationRectificationVoList);
        result.put("totalVo", totalVo);

        return Answer.ok(result);
    }

    @Value("${excel.template.count-light:template/terminationRectification.xlsx}")
    private String terminationRectificationPath;

    @Override
    public void exportTerminationRectification(DeadlineNodeOutput req, HttpServletResponse response,JSONObject reqJson) {
        String projectChildCategoryType = reqJson.getString("projectChildCategoryType");
        // 1. 获取数据（添加非空校验）
        Answer<Object> objectAnswer = this.terminationRectification(reqJson);
        Assert.notNull(objectAnswer, "统计数据查询结果为空");

        Map<String, Object> resultMap = JSON.parseObject(
                JSON.toJSONString(objectAnswer.getResult()),  // 将 Answer 的 data 转为 JSON 字符串
                new TypeReference<Map<String, Object>>() {}    // 泛型声明：key=字符串，value=任意对象
        );

        List<TerminationRectificationVo> departmentList = new ArrayList<>();
        Object deptListObj = resultMap.get("departmentList");
        if (deptListObj != null) {
            // 转换为 List<TerminationRectificationVo>
            departmentList = JSON.parseArray(
                    JSON.toJSONString(deptListObj),
                    TerminationRectificationVo.class
            );
        }

        TerminationRectificationTotalVo totalVo = new TerminationRectificationTotalVo();
        Object totalVoObj = resultMap.get("totalVo");
        if (totalVoObj != null) {
            // 转换为 TerminationRectificationTotalVo
            totalVo = JSON.parseObject(
                    JSON.toJSONString(totalVoObj),
                    TerminationRectificationTotalVo.class
            );
        }
        totalVo.setProjectChildCategoryType(projectChildCategoryType);

        Assert.notEmpty(departmentList, "统计数据列表为空");

        EasyExcelUtil.initResponse(response, "项目列表为空");

        try (
                OutputStream out = response.getOutputStream();  // 响应输出流
                InputStream templateFile = new ClassPathResource(terminationRectificationPath).getInputStream();  // 模板输入流
                com.alibaba.excel.ExcelWriter writer = EasyExcel.write(out).withTemplate(templateFile).build()  // Excel写入器
        ) {

            WriteSheet sheet = EasyExcel.writerSheet().build();

            writer.fill(totalVo, sheet);
            FillConfig fillConfig = FillConfig.builder()
                    .forceNewRow(true)  // 列表数据每条占一行，适合表格型模板
                    .build();

            writer.fill(departmentList, fillConfig, sheet);
        } catch (IOException e) {
            // 记录异常日志（建议使用日志框架如SLF4J）
            throw new RuntimeException("导出平台项目统计表失败：" + e.getMessage(), e);
        }


    }


    @Override
    public Answer<Object> getTodayUpdate(JSONObject reqJson) {
        String date = reqJson.getString("date");
        String startTimeStr = null;
        String endTimeStr = null;
        DateTimeFormatter dateFormatter = DateTimeFormatter.ISO_LOCAL_DATE;
        DateTimeFormatter datetimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // 新增时间格式化器

        if (StringUtils.isNotBlank(date)) {
            try {
                LocalDate localDate = LocalDate.parse(date, dateFormatter);
                LocalDateTime startTime = localDate.atTime(00, 00, 00);
                LocalDateTime endTime = localDate.atTime(23, 59, 59);
                // 手动格式化为字符串
                startTimeStr = startTime.format(datetimeFormatter);
                endTimeStr = endTime.format(datetimeFormatter);
            } catch (Exception e) {
                startTimeStr = null;
                endTimeStr = null;
            }
        }

        List<TodayUpdateVo> todayUpdateVoList = new ArrayList<>();

        // 条件构造器传入 String 类型日期（带单引号，MySQL 识别）
        LambdaQueryWrapper<UpdateProgress> progressWrapper = new LambdaQueryWrapper<>();
        if (startTimeStr != null && endTimeStr != null) {
            progressWrapper.ge(UpdateProgress::getCreateTime, startTimeStr)
                    .le(UpdateProgress::getCreateTime, endTimeStr);
        }
        List<UpdateProgress> updateProgresses = updateProgressMapper.selectList(progressWrapper);

        // DeadlineNodeUpdate 同理，改为 String 类型日期
        LambdaQueryWrapper<DeadlineNodeUpdate> nodeUpdateWrapper = new LambdaQueryWrapper<>();
        if (startTimeStr != null && endTimeStr != null) {
            nodeUpdateWrapper.ge(DeadlineNodeUpdate::getSignTime, startTimeStr)
                    .le(DeadlineNodeUpdate::getSignTime, endTimeStr);
        }
        List<DeadlineNodeUpdate> deadlineNodeUpdates = deadlineNodeUpdateMapper.selectList(nodeUpdateWrapper);

        List<RectificationInfo> rectificationInfoList = rectificationInfoMapper.selectList(new LambdaQueryWrapper<>());
        Map<Integer, RectificationInfo> rectificationMap = rectificationInfoList.stream()
                .filter(info -> info != null && info.getId() != null)
                .collect(Collectors.toMap(
                        RectificationInfo::getId,
                        info -> info,
                        (oldValue, newValue) -> oldValue
                ));

        List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(new LambdaQueryWrapper<>());
        Map<String, DeadlineNode> deadlineNodeMap = deadlineNodeList.stream()
                .filter(node -> node != null && node.getId() != null)
                .collect(Collectors.toMap(
                        node -> String.valueOf(node.getId()),
                        node -> node,
                        (oldValue, newValue) -> oldValue
                ));

        if (CollectionUtils.isNotEmpty(updateProgresses)) {
            for (UpdateProgress updateProgress : updateProgresses) {
                if (updateProgress == null || StringUtils.isEmpty(updateProgress.getRectificationId())) {
                    continue;
                }

                Integer rectId = null;
                try {
                    rectId = Integer.parseInt(updateProgress.getRectificationId());
                } catch (NumberFormatException e) {
                    continue;
                }

                RectificationInfo rectificationInfo = rectificationMap.get(rectId);
                if (rectificationInfo == null) {
                    continue;
                }

                TodayUpdateVo vo = buildTodayUpdateVo(rectificationInfo, "工作进展和存在问题有更新");
                todayUpdateVoList.add(vo);
            }
        }

        if (CollectionUtils.isNotEmpty(deadlineNodeUpdates)) {
            for (DeadlineNodeUpdate deadlineNodeUpdate : deadlineNodeUpdates) {
                if (deadlineNodeUpdate == null || StringUtils.isEmpty(deadlineNodeUpdate.getNodeId())) {
                    continue;
                }

                String nodeId = deadlineNodeUpdate.getNodeId();
                DeadlineNode deadlineNode = deadlineNodeMap.get(nodeId);
                if (deadlineNode == null) {
                    continue;
                }

                Integer rectificationId = deadlineNode.getRectificationId();
                RectificationInfo rectificationInfo = rectificationMap.get(rectificationId);
                if (rectificationInfo == null) {
                    continue;
                }

                TodayUpdateVo vo = buildTodayUpdateVo(rectificationInfo, "备注");
                todayUpdateVoList.add(vo);
            }
        }

        return Answer.ok(todayUpdateVoList);
    }

    private TodayUpdateVo buildTodayUpdateVo(RectificationInfo rectificationInfo, String type) {
        TodayUpdateVo vo = new TodayUpdateVo();
        vo.setTitle(StringUtils.isNotBlank(rectificationInfo.getTitle()) ? rectificationInfo.getTitle() : "");
        String projectType = StringUtils.isNotBlank(rectificationInfo.getProjectType()) ? rectificationInfo.getProjectType() : "";
        String projectChildType = StringUtils.isNotBlank(rectificationInfo.getProjectChildType()) ? rectificationInfo.getProjectChildType() : "";
        String projectChildCategoryType = StringUtils.isNotBlank(rectificationInfo.getProjectChildCategoryType()) ? rectificationInfo.getProjectChildCategoryType() : "";
        vo.setColumn(projectType + projectChildType + projectChildCategoryType);
        vo.setType(type);
        return vo;
    }

    @Override
    public Answer<Object> getOverdueLink() {
        DateTime date = DateUtil.date();
        LambdaQueryWrapper<DeadlineNode> redNodeQuery = new LambdaQueryWrapper<>();
        redNodeQuery.eq(DeadlineNode::getWarnLevel, "2")
                .isNull(DeadlineNode::getEndTime);
        List<DeadlineNode> redNodeList = deadlineNodeMapper.selectList(redNodeQuery);

        List<RectificationInfo> rectificationInfoList = rectificationInfoMapper.selectList(new LambdaQueryWrapper<>());
        Map<Integer, RectificationInfo> rectificationMap = rectificationInfoList.stream()
                .filter(info -> info != null && info.getId() != null)
                .collect(Collectors.toMap(
                        RectificationInfo::getId,
                        info -> info,
                        (oldValue, newValue) -> oldValue
                ));

        List<ReminderPerson> reminderPeople = reminderPersonMapper.selectList(new LambdaQueryWrapper<>());
        Map<String, ReminderPerson> reminderPeopleMap = reminderPeople.stream()
                .filter(info -> info != null && info.getId() != null)
                .collect(Collectors.toMap(
                        ReminderPerson::getPersonName,
                        info -> info,
                        (oldValue, newValue) -> oldValue
                ));
        List<DeadlineNode> filteredRedNodeList = new ArrayList<>();
        for (DeadlineNode redNode : redNodeList) {
            RectificationInfo rectificationInfo = rectificationMap.get(redNode.getRectificationId());
            if (rectificationInfo == null) {
                continue;
            }

            if ("已关闭".equals(rectificationInfo.getState())) {
                continue;
            }

            // 过滤掉progress为"/"的节点
            if (redNode.getProgress() != null && redNode.getProgress().equals("/")) {
                continue;
            }
            filteredRedNodeList.add(redNode);
        }
        List<OverdueLinkVo> overdueLink = new ArrayList<>();
        for (DeadlineNode redNode : filteredRedNodeList) {
            Date deadline = redNode.getDeadlineRectificationPeriod();
            if (deadline == null) {
                deadline = redNode.getDeadline();
            }

            if (deadline == null) {
                continue;
            }

            long hoursBetween = DateUtil.between(deadline, date, DateUnit.HOUR);

            if (hoursBetween >= 120) {
                RectificationInfo rectificationInfo = rectificationMap.get(redNode.getRectificationId());
                if (rectificationInfo != null) {
                    ReminderPerson reminderPerson = reminderPeopleMap.get(redNode.getDescription());
                    OverdueLinkVo overdueLinkVo = new OverdueLinkVo();
                    String projectType = StringUtils.isNotBlank(rectificationInfo.getProjectType()) ? rectificationInfo.getProjectType() : "";
                    String projectChildType = StringUtils.isNotBlank(rectificationInfo.getProjectChildType()) ? rectificationInfo.getProjectChildType() : "";
                    String projectChildCategoryType = StringUtils.isNotBlank(rectificationInfo.getProjectChildCategoryType()) ? rectificationInfo.getProjectChildCategoryType() : "";
                    overdueLinkVo.setColumn(projectType + projectChildType + projectChildCategoryType);
                    overdueLinkVo.setTitle(rectificationInfo.getTitle());
                    overdueLinkVo.setName(redNode.getName());
                    overdueLinkVo.setDeadline(redNode.getDeadline());
                    overdueLinkVo.setOverdueTime(String.valueOf(hoursBetween/24));
                    overdueLinkVo.setDepartmentName(rectificationInfo.getDepartmentName());
                    if(StrUtil.isEmptyIfStr(redNode.getDescription())){
                        overdueLinkVo.setDescription(rectificationInfo.getPersonLiaible());
                        ReminderPerson reminderPerson2 = reminderPeopleMap.get(rectificationInfo.getPersonLiaible());
                        if (reminderPerson2!=null) {
                            overdueLinkVo.setPhone(reminderPerson2.getPhone());
                        }
                    }else{
                        overdueLinkVo.setDescription(redNode.getDescription());
                        if (reminderPerson!=null) {
                            overdueLinkVo.setPhone(reminderPerson.getPhone());
                        }
                    }
                    overdueLink.add(overdueLinkVo);
                }
            }
        }
        return Answer.ok(overdueLink);
    }



    /**
     * 按部门查树,只要二级
     * @return
     */
    @Override
    public Answer<Object> rectificationAnalyzeByCity() {
        List<RectificationInfo> rectificationInfoList = fetchAllRectification("");
        // 过滤区划的
        // 将 A部门B部门C区划拆分成三个,补充进去
//      只保留县市区的数据
        rectificationInfoList = RectificationClassifier.process(rectificationInfoList);
//        List<DeadlineRectificationPeriod> deadlineRectificationPeriods = deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<>());
//        for (int index = 0; index < rectificationInfoList.size(); index++) {
//            RectificationInfo rectificationInfo1 = rectificationInfoList.get(index);
//            List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(
//                    new LambdaQueryWrapper<DeadlineNode>()
//                            .eq(DeadlineNode::getRectificationId, rectificationInfo1.getId())
//                            .orderByDesc(DeadlineNode::getId)
//            );
//            deadlineNodeList = filiterNode(rectificationInfo1, deadlineNodeList);
//            rectificationInfo1 = calcWarnLevel(rectificationInfo1,deadlineNodeList,deadlineRectificationPeriods);
//            rectificationInfoList.get(index).setWarnLevel(rectificationInfo1.getWarnLevel());
//            rectificationInfoList.get(index).setCurrentNode(rectificationInfo1.getCurrentNode());
//        }
        // 将这个数据按照树的结构返回
        RectificationFourLevelClassifier classifier = new RectificationFourLevelClassifier();
        List<RectificationFourLevelClassifier.DepartmentNode> fourLevelTree =
                classifier.generateDepartmentProjectStructure(rectificationInfoList);
        // 移除total为空的节点

        fourLevelTree = fourLevelTree.stream().filter(project -> project.getTotal() > 0).collect(Collectors.toList());
        // 根据部门,按照 红灯率由低到高排序
        // 使用Comparator实现自定义排序
        fourLevelTree.sort(new Comparator<RectificationFourLevelClassifier.DepartmentNode>() {
            @Override
            public int compare(RectificationFourLevelClassifier.DepartmentNode deptNode1, RectificationFourLevelClassifier.DepartmentNode deptNode2) {
                // 1. 提取两个部门节点的红灯率字符串，并转换为数值
                double redRate1 = RectificationFourLevelClassifier.convertRedRateToDouble(deptNode1.getTotalRedRate());
                double redRate2 = RectificationFourLevelClassifier.convertRedRateToDouble(deptNode2.getTotalRedRate());
                // 2. 按数值升序排序（低→高）
                return Double.compare(redRate1, redRate2);
            }
        });
        return Answer.ok(fourLevelTree);
    }

    /** 节点和 项目合并后
     * @return
     */
    @Override
    public Answer<Object> rectificationDeadlineAnalyzeByCity() {

        List<RectificationInfo> rectificationInfoList = fetchAllOnlyNodeRectification("");
        // 过滤区划的
        // 将 A部门B部门C区划拆分成三个,补充进去
//        例如 黄冈高新区市生态环境局国投集团拆成三个
        List<DeathLineNodeDetailVO> deadlineNodes = new ArrayList<>();
        for (int index = 0; index < rectificationInfoList.size(); index++) {
            RectificationInfo rectificationInfo1 = rectificationInfoList.get(index);
            List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(
                    new LambdaQueryWrapper<DeadlineNode>()
                            .eq(DeadlineNode::getRectificationId, rectificationInfo1.getId())
                            .orderByDesc(DeadlineNode::getId)
            );
            deadlineNodeList = filiterNode(rectificationInfo1, deadlineNodeList);
            boolean isCityLevelDepartment = !COUNTY_LEVEL_DISTRICTS2.contains(rectificationInfo1.getDepartmentName());
            for (DeadlineNode node : deadlineNodeList) {
                String deptName = node.getDepartmentName();
                if (deptName == null) continue; // 跳过部门名为空的节点
                if (!isCityLevelDepartment){
                    continue;
                }
                // 部门名称标准化（统一格式）
                deptName = deptName.replace("发改","市发改委");
                deptName = deptName.replace("资建","市资建局");
                deptName = deptName.replace("林业","市林业局");
                deptName = deptName.replace("财政","市财政局");
                deptName = deptName.replace("生态","市生态环境局");
                deptName = deptName.replace("应急","市应急管理局");
                deptName = deptName.replace("自规部门","市自然资源规划局");
                deptName =  deptName.replace("住建","市住新局");
                node.setDepartmentName(deptName);
                if (StrUtil.isNotBlank(deptName)){
                    DeathLineNodeDetailVO deathLineNodeDetailVO = new DeathLineNodeDetailVO();
                    deathLineNodeDetailVO.setDepartment(deptName);
                    deathLineNodeDetailVO.setProjectType(rectificationInfo1.getProjectType());
                    deathLineNodeDetailVO.setProjectChildType(rectificationInfo1.getProjectChildType());
                    if (StrUtil.isEmptyIfStr(rectificationInfo1.getProjectChildCategoryType())){
                        deathLineNodeDetailVO.setProjectChildCategoryType(rectificationInfo1.getProjectChildType());
                    }else{
                        deathLineNodeDetailVO.setProjectChildCategoryType(rectificationInfo1.getProjectChildCategoryType());
                    }
                    if (node.getWarnLevel()==null){
                        node.setWarnLevel("0");
                    }
                    deathLineNodeDetailVO.setWarnLevel(Integer.valueOf(node.getWarnLevel()));
                    deadlineNodes.add(deathLineNodeDetailVO);
                }
            }
        }
        deadlineNodes = SmartDepartmentSplitter.splitNodes2(deadlineNodes);
        out.println(deadlineNodes.size());
        List<DepartmentSummary> result = SmartDepartmentSplitter.groupByDepartmentAndProject(deadlineNodes);
        out.println("分组结果: " + result);


        result.sort(Comparator.comparing(
                // 1. 提取并转换红灯率为 BigDecimal（确保精度，避免浮点数误差）
                region -> {
                    String totalRedRate = region.getTotalRedRate();
                    // 处理空值/空字符串：默认按最大数值（排最后）
                    if (totalRedRate == null || totalRedRate.trim().isEmpty()) {
                        return new BigDecimal(Double.MAX_VALUE);
                    }
                    try {
                        // 去除百分号，转为 BigDecimal（如 "15.50%" → "15.50" → 15.50）
                        String rateNumStr = totalRedRate.trim().replace("%", "");
                        return new BigDecimal(rateNumStr);
                    } catch (NumberFormatException e) {
                        // 处理格式异常（如 "abc%"）：默认按最大数值（排最后）
                        return new BigDecimal(Double.MAX_VALUE);
                    }
                },
                // 2. 按红灯率数值升序排序（由低到高）
                Comparator.naturalOrder()
        ));

        result.sort((r1, r2) -> {
            // 获取两个对象的红灯率字符串
            String rate1 = r1.getTotalRedRate();
            String rate2 = r2.getTotalRedRate();

            // 处理空值情况：空值排在最后
            if (rate1 == null || rate1.trim().isEmpty()) {
                return 1; // r1空，r1排在r2后面
            }
            if (rate2 == null || rate2.trim().isEmpty()) {
                return -1; // r2空，r1排在r2前面
            }

            try {
                // 提取百分比数值（去除百分号并转换为double）
                double value1 = Double.parseDouble(rate1.trim().replace("%", ""));
                double value2 = Double.parseDouble(rate2.trim().replace("%", ""));

                // 按数值升序排序（由低到高）
                return Double.compare(value1, value2);
            } catch (NumberFormatException e) {
                // 处理格式错误：格式错误的排在后面
                // 如果两个都格式错误，保持相对顺序
                return 0;
            }
        });

        return Answer.ok(result);
    }

    @Override
    public Answer<Object> rectificationAnalyze() {
        List<RectificationInfo> rectificationInfoList = fetchAllRectification("");
//        List<DeadlineRectificationPeriod> deadlineRectificationPeriods = deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<>());
//        for (RectificationInfo rectificationInfoItem : rectificationInfoList) {
//            // ---------------------- 原有currentNode和warnLevel计算逻辑（不变）----------------------
//            List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(
//                    new LambdaQueryWrapper<DeadlineNode>()
//                            .eq(DeadlineNode::getRectificationId, rectificationInfoItem.getId())
//                            .orderByDesc(DeadlineNode::getId)
//            );
//            deadlineNodeList = filiterNode(rectificationInfoItem,deadlineNodeList);
//            RectificationInfo  rectificationInfoItem2 = calcWarnLevel(rectificationInfoItem,deadlineNodeList,deadlineRectificationPeriods);
//            rectificationInfoItem.setWarnLevel(rectificationInfoItem2.getWarnLevel());
//        }
        List<RectificationAnalyze> rectificationAnalyzeList
                = RectificationTypeConverter.convertAndGroupByChildCategory(rectificationInfoList);
        return Answer.ok(rectificationAnalyzeList);
    }

    @Override
    public Answer<Object> getRectificationInfoXF() {
        RectificationInfo rectificationInfo = new RectificationInfo();
        rectificationInfo.setProjectChildType("信访积案化解");
        Answer<Object> objectAnswer = this.queryRectificationListV2(rectificationInfo);
        RectificationListVo result = (RectificationListVo)objectAnswer.getResult();
        List<RectificationInfo> dataList = result.getDataList();
        int red = 0;
        int yellow = 0;
        int green = 0;
        int state = 0;
        Date date = new Date();
        RectificationInfoXF rectificationInfoXF = new RectificationInfoXF();
        List<RectificationInfoXF> rectificationInfoXFS = new ArrayList<>();
        for (RectificationInfo r : dataList){
            if (r.getWarnLevel()==0){
                green++;
            }else if (r.getWarnLevel()==1){
                yellow++;
            }else{
                red++;
            }
            if ("已完成".equals(r.getState())){
                state++;
            }
            RectificationInfoXF rectificationInfoXFR = new RectificationInfoXF();
            rectificationInfoXFR.setPetitioner(r.getPetitioner());
            rectificationInfoXFR.setAppealType(r.getAppealType());
            rectificationInfoXFR.setCurrentNode(r.getCurrentNode());
            Answer<Object> objectAnswerNode = this.queryDeadlineNodeList(r);
            List<DeadlineNode> resultNode = (List<DeadlineNode>)objectAnswerNode.getResult();
            for (DeadlineNode node : resultNode){
                if (node.getName().equals(r.getCurrentNode())){
                    rectificationInfoXFR.setDeadline(node.getDeadline());
                }
                rectificationInfoXFR.setToday(date);
                if (rectificationInfoXFR.getActualNode()!=null){
                    if (node.getEndTime() != null) {
                        rectificationInfoXFR.setActualNode(rectificationInfoXFR.getActualNode()+","+node.getName());
                    }
                }else {
                    if (node.getEndTime() != null) {
                        rectificationInfoXFR.setActualNode(node.getName());
                    }
                }
            }
            rectificationInfoXFR.setContent(r.getContent());
            rectificationInfoXFR.setLeadUnit(r.getLeadUnit());
            rectificationInfoXFR.setLeadLeader(r.getLeadLeader());
            rectificationInfoXFR.setDepartmentName(r.getDepartmentName());
            rectificationInfoXFS.add(rectificationInfoXFR);
        }
        rectificationInfoXF.setToday(date);
        rectificationInfoXF.setTotal(dataList.size());
        rectificationInfoXF.setState(state);
        rectificationInfoXF.setNormalNum(green+yellow);
        rectificationInfoXF.setRedNum(red);
        rectificationInfoXF.setRectificationInfoXFList(rectificationInfoXFS);
        return Answer.ok(rectificationInfoXF);
    }

    @Override
    public Answer<Object> queryRectificationPageV2(JSONObject reqJson) {
        try {
            LambdaQueryWrapper<RectificationInfo> wrapper = new LambdaQueryWrapper<>();
            String state = reqJson.getString("state");
            String projectType = reqJson.getString("projectType");
            String areaName = reqJson.getString("areaName");
            String leadUnit = reqJson.getString("leadUnit");
            String departmentName = reqJson.getString("departmentName");
            String projectChildType = reqJson.getString("projectChildType");
            JSONArray projectChildTypeList = reqJson.getJSONArray("projectChildTypeList");
            String projectChildCategoryType = reqJson.getString("projectChildCategoryType");
            String title = reqJson.getString("title");
            String personLiaible = reqJson.getString("personLiaible");
            String leadLeader = reqJson.getString("leadLeader");
            String currentDepartment = reqJson.getString("currentDepartment");
            String currentNode = reqJson.getString("currentNode");
            String statusType = reqJson.getString("statusType");
            Integer warnLevel = reqJson.getInteger("warnLevel");
            String label = reqJson.getString("label");

            Integer pageSize = reqJson.getInteger("pageSize");
            Integer pageNum = reqJson.getInteger("pageNum");
            Page<RectificationInfo> resultPage = new Page<>(pageNum, pageSize);

            // 1. 获取所有数据
            List<RectificationInfo> rectificationInfoList = fetchAllRectification(currentDepartment);

            // 2. 预先获取所有可能需要的数据
            List<Integer> allIds = rectificationInfoList.stream()
                    .map(RectificationInfo::getId)
                    .collect(Collectors.toList());

            List<Integer> rectificationIdList =new ArrayList<>();
            List<DeadlineNode> deadlineNodes = new ArrayList<>();
            //查询满足责任人的全部节点
            if (StringUtils.isNotBlank(personLiaible)) {
                // 1. 提取所有整改单ID（避免循环查库）
                List<Integer> rectificationIds = rectificationInfoList.stream()
                        .map(RectificationInfo::getId)
                        .collect(Collectors.toList());
                // 2. 一次查询所有符合条件的节点（IN查询+条件过滤）
                LambdaQueryWrapper<DeadlineNode> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.like(DeadlineNode::getDescription, personLiaible)
                        .in(DeadlineNode::getRectificationId, rectificationIds) // 批量查询
                        .orderByDesc(DeadlineNode::getId);

                List<DeadlineNode> allDeadlineNodes = deadlineNodeMapper.selectList(queryWrapper);
                // 3. 按整改单ID分组（内存关联，避免循环查库）
                Map<Integer, List<DeadlineNode>> nodeGroupByRectId = allDeadlineNodes.stream()
                        .collect(Collectors.groupingBy(DeadlineNode::getRectificationId));

                // 4. 遍历整改单，关联对应的节点并过滤
                for (RectificationInfo rectificationInfo : rectificationInfoList) {
                    Integer rectId = rectificationInfo.getId();
                    // 获取当前整改单对应的节点列表（无数据则返回空列表）
                    List<DeadlineNode> nodes = nodeGroupByRectId.getOrDefault(rectId, Collections.emptyList());
                    // 过滤节点并添加到结果集
                    deadlineNodes.addAll(filiterNode(rectificationInfo, nodes));
                }
//                for (int index = 0; index < rectificationInfoList.size(); index++) {
//                    RectificationInfo rectificationInfo1 = rectificationInfoList.get(index);
//                    LambdaQueryWrapper<DeadlineNode> lambdaQueryWrapper = new LambdaQueryWrapper();
//                    lambdaQueryWrapper.like(DeadlineNode::getDescription, personLiaible);
//                    lambdaQueryWrapper.eq(DeadlineNode::getRectificationId,rectificationInfo1.getId());
//                    lambdaQueryWrapper.orderByDesc(DeadlineNode::getId);
//                    List<DeadlineNode> deadlineNodes2 = deadlineNodeMapper.selectList(lambdaQueryWrapper);
//                    deadlineNodes2 = filiterNode(rectificationInfo1, deadlineNodes2);
//                    deadlineNodes.addAll(deadlineNodes2);
//                }
                rectificationIdList = deadlineNodes.stream()
                        // 过滤掉null值（避免空指针或后续处理问题）
                        .filter(node -> node.getRectificationId() != null)
                        // 提取rectificationId字段
                        .map(DeadlineNode::getRectificationId)
                        .distinct()
                        // 转换为List
                        .collect(Collectors.toList());
            }

            // 创建final或effectively final的Map变量
            final Map<Integer, List<DeadlineNode>> finalNodeMap;

            if (CollectionUtil.isNotEmpty(allIds)) {
                // 批量查询所有相关的DeadlineNode
                List<DeadlineNode> allNodes = deadlineNodeMapper.selectList(
                        new LambdaQueryWrapper<DeadlineNode>()
                                .in(DeadlineNode::getRectificationId, allIds)
                                .orderByDesc(DeadlineNode::getId)
                );
                finalNodeMap = allNodes.stream()
                        .collect(Collectors.groupingBy(DeadlineNode::getRectificationId));
            } else {
                finalNodeMap = Collections.emptyMap();
            }

            // 获取所有DeadlineRectificationPeriods
            final List<DeadlineRectificationPeriod> finalDeadlinePeriods =
                    deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<DeadlineRectificationPeriod>()
                            .eq(DeadlineRectificationPeriod::getIsApproval,1).orderByDesc(DeadlineRectificationPeriod::getId));
            // 3. 使用流操作一次性应用所有过滤条件
            Stream<RectificationInfo> stream = rectificationInfoList.stream();

            if (StrUtil.isNotBlank(projectType)) {
                stream = stream.filter(item -> projectType.equals(item.getProjectType()));
            }
            if (StrUtil.isNotBlank(projectChildType)) {
                stream = stream.filter(item ->
                        item.getProjectChildType() != null &&
                                item.getProjectChildType().contains(projectChildType));
            }
            if (StrUtil.isNotBlank(projectChildCategoryType)) {
                stream = stream.filter(item ->
                        item.getProjectChildCategoryType() != null &&
                                item.getProjectChildCategoryType().equals(projectChildCategoryType));
            }
            if (StrUtil.isNotBlank(departmentName)) {
                stream = stream.filter(item ->
                        item.getDepartmentName() != null &&
                                item.getDepartmentName().contains(departmentName));
            }
            if (StrUtil.isNotBlank(title)) {
                stream = stream.filter(item ->
                        item.getTitle() != null &&
                                item.getTitle().equals(title));
            }
            if (StrUtil.isNotBlank(personLiaible)) {
                // 确保rectificationIdList不为null，避免NullPointerException
                List<?> safeIdList = rectificationIdList != null ? rectificationIdList : Collections.emptyList();

                stream = stream.filter(item -> {
                    // 避免item为null导致的空指针异常
                    if (item == null) {
                        return false;
                    }

                    // 条件1：personLiaible匹配
                    boolean personMatch = item.getPersonLiaible() != null &&
                            item.getPersonLiaible().equals(personLiaible);

                    // 条件2：id在列表中
                    boolean idMatch = item.getId() != null &&
                            safeIdList.contains(item.getId());

                    return personMatch || idMatch;
                });
            }
            if (StrUtil.isNotBlank(leadLeader)) {
                stream = stream.filter(item -> item.getLeadLeader() != null &&
                        item.getLeadLeader().contains(leadLeader));
            }
            if (StrUtil.isNotBlank(label)) {
                stream = stream.filter(item -> item.getLabel() != null &&
                        item.getLabel().contains(label));
            }
            if (StrUtil.isNotBlank(areaName)) {
                final List<String> departments = Arrays.asList(
                        "黄州区", "团风县", "红安县", "麻城市",
                        "罗田县", "英山县", "浠水县", "蕲春县",
                        "武穴市", "黄梅县", "龙感湖管理区", "白莲河示范区",
                        "黄冈高新区","市临空经济区"
                );

                if ("市本级".equals(areaName)) {
                    stream = stream.filter(item ->
                            item.getAreaName() != null &&
                                    !departments.contains(item.getAreaName()));
                } else {
                    stream = stream.filter(item ->
                            item.getAreaName() != null &&
                                    item.getAreaName().equals(areaName));
                }
            }
            if (projectChildTypeList != null && projectChildTypeList.size() > 0) {
                final List<String> finalTypeList = projectChildTypeList.toJavaList(String.class);
                stream = stream.filter(item ->
                        item.getProjectChildType() != null &&
                                finalTypeList.contains(item.getProjectChildType()));
            }

            // 应用过滤条件
                rectificationInfoList = stream.collect(Collectors.toList());

            // 4. 并行处理警告级别计算
            if (CollectionUtil.isNotEmpty(rectificationInfoList)) {
                rectificationInfoList = rectificationInfoList.parallelStream()
                        .map(rectificationInfo1 -> {
                            List<DeadlineNode> deadlineNodeList = finalNodeMap.getOrDefault(rectificationInfo1.getId(), new ArrayList<>());
                            deadlineNodeList = filiterNode(rectificationInfo1, deadlineNodeList);
                            RectificationInfo updatedInfo = calcWarnLevel(rectificationInfo1, deadlineNodeList, finalDeadlinePeriods);
                            rectificationInfo1.setWarnLevel(updatedInfo.getWarnLevel());
                            rectificationInfo1.setCurrentNode(updatedInfo.getCurrentNode());
                            return rectificationInfo1;
                        })
                        .collect(Collectors.toList());
            }

            // 5. 应用警告级别和当前节点过滤
            Stream<RectificationInfo> finalStream = rectificationInfoList.stream();

            final Integer finalWarnLevel = warnLevel;
            if (finalWarnLevel != null) {
                finalStream = finalStream.filter(item ->
                        item.getWarnLevel() != null &&
                                item.getWarnLevel().equals(finalWarnLevel));
            }

            final String finalCurrentNode = currentNode;
            if (StrUtil.isNotBlank(finalCurrentNode)) {
                finalStream = finalStream.filter(item ->
                        item.getCurrentNode() != null &&
                                item.getCurrentNode().equals(finalCurrentNode));
            }

            rectificationInfoList = finalStream.collect(Collectors.toList());

            // 6. 手动分页优化
            int total = rectificationInfoList.size();
            int pages = (total + pageSize - 1) / pageSize;

            if (pageNum > pages) {
                pageNum = pages;
            }
            if (pageNum < 1) {
                pageNum = 1;
            }

            int fromIndex = (pageNum - 1) * pageSize;
            int toIndex = Math.min(fromIndex + pageSize, total);

            // 使用子列表创建分页结果
            List<RectificationInfo> pageRecords = rectificationInfoList.subList(fromIndex, toIndex);

            resultPage.setRecords(pageRecords);
            resultPage.setTotal(total);
            resultPage.setPages(pages);
            resultPage.setCurrent(pageNum);
            resultPage.setSize(pageSize);

            return Answer.ok(resultPage);
        } catch (Exception e) {
            return Answer.error("查询失败: " + e.getMessage());
        }
    }

    // 计算已经完成的数和红绿灯数量
    @Override
    public Answer<Object> queryRectificationStaticV2(RectificationInfo rectificationInfo) {

        // 1. 并行获取基础数据
        String currentDepartment = rectificationInfo.getCurrentDepartment();

// 使用并行处理获取所有数据
        List<RectificationInfo> rectificationInfoList = fetchAllRectification(currentDepartment);
        List<DeadlineRectificationPeriod> deadlineRectificationPeriods = deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<>());

// 2. 提取过滤条件
        String projectType = rectificationInfo.getProjectType();
        Integer warnLevel = rectificationInfo.getWarnLevel();
        String projectChildType = rectificationInfo.getProjectChildType();
        String projectChildCategoryType = rectificationInfo.getProjectChildCategoryType();
        String departmentName = rectificationInfo.getDepartmentName();
        String personLiaible = rectificationInfo.getPersonLiaible();
        String leadLeader = rectificationInfo.getLeadLeader();

// 3. 使用单个流操作应用所有过滤条件
        Stream<RectificationInfo> stream = rectificationInfoList.stream();

        if (StrUtil.isNotBlank(projectType)) {
            stream = stream.filter(item -> projectType.equals(item.getProjectType()));
        }
        if (StrUtil.isNotBlank(projectChildType)) {
            stream = stream.filter(item -> item.getProjectChildType() != null &&
                    item.getProjectChildType().contains(projectChildType));
        }
        if (StrUtil.isNotBlank(projectChildCategoryType)) {
            stream = stream.filter(item -> item.getProjectChildCategoryType() != null &&
                    item.getProjectChildCategoryType().equals(projectChildCategoryType));
        }
        if (StrUtil.isNotBlank(departmentName)) {
            stream = stream.filter(item -> item.getDepartmentName() != null &&
                    item.getDepartmentName().contains(departmentName));
        }
        if (StrUtil.isNotBlank(personLiaible)) {
            stream = stream.filter(item -> item.getPersonLiaible() != null &&
                    item.getPersonLiaible().contains(personLiaible));
        }
        if (StrUtil.isNotBlank(leadLeader)) {
            stream = stream.filter(item -> item.getLeadLeader() != null &&
                    item.getLeadLeader().contains(leadLeader));
        }

        List<RectificationInfo> filteredList = stream.collect(Collectors.toList());

// 4. 批量获取所有相关的DeadlineNode数据
        List<Integer> rectificationIds = filteredList.stream()
                .map(RectificationInfo::getId)
                .collect(Collectors.toList());

// 创建final或effectively final的Map变量
        final Map<Integer, List<DeadlineNode>> finalNodeMap;

        if (CollectionUtil.isNotEmpty(rectificationIds)) {
            // 批量查询所有相关的DeadlineNode
            List<DeadlineNode> allNodes = deadlineNodeMapper.selectList(
                    new LambdaQueryWrapper<DeadlineNode>()
                            .in(DeadlineNode::getRectificationId, rectificationIds)
                            .orderByDesc(DeadlineNode::getId)
            );

            // 按rectificationId分组
            finalNodeMap = allNodes.stream()
                    .collect(Collectors.groupingBy(DeadlineNode::getRectificationId));
        } else {
            finalNodeMap = Collections.emptyMap();
        }

// 创建final引用，用于Lambda表达式
        final Integer finalWarnLevel = warnLevel;

// 5. 使用并行流处理统计计算
// 创建一个专门的统计类来收集结果
        RectificationStatistics statistics = filteredList.parallelStream()
                .filter(rectificationInfo1 -> finalWarnLevel == null ||
                        rectificationInfo1.getWarnLevel().intValue() == finalWarnLevel.intValue())
                .collect(RectificationStatistics::new,
                        (stats, rectificationInfo1) -> {
                            // 统计warn level
                            if(rectificationInfo1.getWarnLevel() == null){
                                stats.green++;
                            }else{
                                switch (rectificationInfo1.getWarnLevel()) {
                                    case 0: stats.green++; break;
                                    case 1: stats.yellow++; break;
                                    case 2: stats.red++; break;
                                }
                            }

                            // 计算完成和超期状态
                            List<DeadlineNode> deadlineNodeList = finalNodeMap.getOrDefault(rectificationInfo1.getId(), new ArrayList<>());
                            boolean isCompleted = StrUtil.isEmpty(rectificationInfo1.getCurrentNode()) &&
                                    CollectionUtil.isNotEmpty(deadlineNodeList);
                            boolean isOverdue = rectificationInfo1.getWarnLevel() == 2;

                            if (isCompleted && rectificationInfo1.getState().equals("已销号")) {
                                if (isOverdue) {
                                    stats.completationAndOverdue++;
                                } else {
                                    stats.completationAndNotOverdue++;
                                }
                            } else {
                                if (isOverdue) {
                                    stats.notCompletationAndOverdue++;
                                } else {
                                    stats.notCompletationAndNotOverdue++;
                                }
                            }

                            stats.total++;
                        },
                        (stats1, stats2) -> {
                            // 合并统计结果
                            stats1.completationAndNotOverdue += stats2.completationAndNotOverdue;
                            stats1.completationAndOverdue += stats2.completationAndOverdue;
                            stats1.notCompletationAndNotOverdue += stats2.notCompletationAndNotOverdue;
                            stats1.notCompletationAndOverdue += stats2.notCompletationAndOverdue;
                            stats1.total += stats2.total;
                            stats1.green += stats2.green;
                            stats1.yellow += stats2.yellow;
                            stats1.red += stats2.red;
                        });

// 6. 构建返回结果
        RectificationListVo result = new RectificationListVo();
        result.setCompletationAndNotOverdue(statistics.completationAndNotOverdue);
        result.setCompletationAndOverdue(statistics.completationAndOverdue);
        result.setNotCompletationAndNotOverdue(statistics.notCompletationAndNotOverdue);
        result.setNotCompletationAndOverdue(statistics.notCompletationAndOverdue);
        result.setTotal(statistics.total);
        result.setGreen(statistics.green);
        result.setYellow(statistics.yellow);
        result.setRed(statistics.red);
        return Answer.ok(result);
    }


    @Override
    public Answer<Object> selectDepartmentNameList() {
        List<RectificationInfoDirTreeVo> rectificationInfoDirTreeVos = rectificationInfoMapper.selectDepartmentNameList();
        int count = 0;
        for (RectificationInfoDirTreeVo r : rectificationInfoDirTreeVos) {
            count += r.getCount();
            r.setChild(null);
        }
        RectificationInfoDirTreeVo rectificationInfoDirTreeVo = new RectificationInfoDirTreeVo();
        rectificationInfoDirTreeVo.setName("黄冈市");
        rectificationInfoDirTreeVo.setCount(count);
        rectificationInfoDirTreeVo.setChild(rectificationInfoDirTreeVos);
        return Answer.ok(rectificationInfoDirTreeVo);
    }

    @Override
    public Answer<String> getLinkTotal() {
        try {
            String http = "http://ihglive.ihuanggang.cn:7050/api/system/project/list";
            Answer<String> link = GetLinkUtil.getLink(http);
            String result = link.getResult();
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode jsonNode = objectMapper.readTree(result);
            JsonNode total = jsonNode.get("total");
            return Answer.ok(total.toString());
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void exportRectificationList(JSONObject reqJson, HttpServletResponse response) {
        // 1. 空值防护：避免数据为null时流处理报错
        List<RectificationInfo> rectificationInfoList = exprotList(reqJson);
        if (rectificationInfoList == null || rectificationInfoList.isEmpty()) {
            throw new RuntimeException("未查询到导出数据");
        }

        // 2. 转换数据为导出格式
        List<Map<String, Object>> rows = convertStatisticsToExportMap(rectificationInfoList);
        int isMultiSheet = (int) rows.get(0).get("isMultiSheet");

        String fileName = "项目列表导出" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + ".xlsx";
        ExcelWriter writer = ExcelUtil.getBigWriter();
        try {
            // 3. 配置表头（别名映射，确保无重复）
            LinkedHashMap<String, String> header = createStatisticsHeaderMap(isMultiSheet);
            header.forEach(writer::addHeaderAlias);
            writer.setOnlyAlias(true); // 仅导出配置的别名列

            // 4. 设置工作簿和工作表对象
            Workbook wb = writer.getWorkbook();
            Sheet sheet = writer.getSheet();

            // 5. 创建自动换行样式（统一应用到第二列所有行：第三行+后续数据行）
            CellStyle wrapStyle = wb.createCellStyle();
            wrapStyle.setWrapText(true); // 启用自动换行
            wrapStyle.setVerticalAlignment(VerticalAlignment.TOP); // 顶部对齐
            // 设置边框，保持表格美观
            wrapStyle.setBorderTop(BorderStyle.THIN);
            wrapStyle.setBorderBottom(BorderStyle.THIN);
            wrapStyle.setBorderLeft(BorderStyle.THIN);
            wrapStyle.setBorderRight(BorderStyle.THIN);

            // 6. 配置标题行（合并+样式）
            String title = "统计分析任务列表";
            writer.merge(header.size() - 1, title); // 合并第0行的0~header.size()-1列
            Row titleRow = sheet.getRow(0);
            Cell titleCell = titleRow.getCell(0);

            // 标题样式（仅应用于标题单元格，不影响其他行）
            CellStyle titleStyle = wb.createCellStyle();
            titleStyle.setAlignment(HorizontalAlignment.CENTER);
            titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            titleStyle.setWrapText(true); // 标题也可能换行
            Font titleFont = wb.createFont();
            titleFont.setBold(true);
            titleFont.setFontHeightInPoints((short) 12);
            titleStyle.setFont(titleFont);
            titleCell.setCellStyle(titleStyle);

            // 写入表头
            List<Map<String, String>> headerlist = new ArrayList<>();
            headerlist.add(header);
            writer.write(headerlist, false);

            // 写入数据
            writer.setCurrentRow(2);
            writer.write(rows, false);

            // 关键改动1：统一处理第二列的所有行（第三行+后续数据行）
            int thirdRowNum = 2; // 第三行（行索引2）
            int dataStartRow = 3; // 数据行起始行号
            int dataCount = rows.size(); // 实际数据行数

            // ① 单独处理「第二列第三行」（行2、列1），应用统一样式
            Row thirdRow = sheet.getRow(thirdRowNum);
            if (thirdRow != null) {
                Cell secondColThirdRowCell = thirdRow.getCell(1); // 第二列（索引1）
                if (secondColThirdRowCell != null) {
                    secondColThirdRowCell.setCellStyle(wrapStyle); // 与数据行样式一致
                } else {
                    // 若单元格为空，创建单元格后再应用样式（避免空单元格无样式）
                    secondColThirdRowCell = thirdRow.createCell(1);
                    secondColThirdRowCell.setCellStyle(wrapStyle);
                }
            }

            // ② 处理后续数据行的第二列（保持原有逻辑，确保统一）
            for (int rowNum = dataStartRow; rowNum < dataStartRow + dataCount; rowNum++) {
                Row dataRow = sheet.getRow(rowNum);
                if (dataRow == null) continue;
                Cell targetCell = dataRow.getCell(1); // 第二列（索引1）
                if (targetCell != null) {
                    targetCell.setCellStyle(wrapStyle);
                } else {
                    // 空单元格也创建并应用样式，确保格式统一
                    targetCell = dataRow.createCell(1);
                    targetCell.setCellStyle(wrapStyle);
                }
            }

            // 关键改动2：清理超出数据范围的空行样式
            int lastRowNum = sheet.getLastRowNum();
            for (int rowNum = dataStartRow + dataCount; rowNum <= lastRowNum; rowNum++) {
                Row emptyRow = sheet.getRow(rowNum);
                if (emptyRow == null) continue;
                for (int colNum = 0; colNum < emptyRow.getLastCellNum(); colNum++) {
                    Cell cell = emptyRow.getCell(colNum);
                    if (cell != null) {
                        cell.setCellStyle(wb.createCellStyle()); // 重置为默认样式
                    }
                }
            }

            // 关键：取消第三列第三行的居中（保留之前的需求）
            Row thirdRowForNoCenter = sheet.getRow(2); // 第三行（行索引2）
            if (thirdRowForNoCenter != null) {
                Cell thirdColCell = thirdRowForNoCenter.getCell(2); // 第三列（索引2）
                if (thirdColCell != null) {
                    CellStyle noCenterStyle = wb.createCellStyle();
                    noCenterStyle.setAlignment(HorizontalAlignment.GENERAL); // 默认对齐（非居中）
                    noCenterStyle.setVerticalAlignment(VerticalAlignment.CENTER);
                    // 保留边框和自动换行，与其他单元格一致
                    noCenterStyle.setBorderTop(BorderStyle.THIN);
                    noCenterStyle.setBorderBottom(BorderStyle.THIN);
                    noCenterStyle.setBorderLeft(BorderStyle.THIN);
                    noCenterStyle.setBorderRight(BorderStyle.THIN);
                    noCenterStyle.setWrapText(true);
                    thirdColCell.setCellStyle(noCenterStyle);
                }
            }

            // 8. 冻结前2行（标题行+表头行，滚动时固定）
            sheet.createFreezePane(0, 2);

            // 9. 设置列宽（第二列加宽以适应多行文本，统一宽度）
            int i = 0;
            for (String key : header.keySet()) {
                int width = (i == 1) ? 40 : 20; // 第二列（索引1）宽度40，其他20
                writer.setColumnWidth(i++, width);
            }

            // 10. 响应配置（避免中文乱码）
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + URLEncoder.encode(fileName, java.nio.charset.StandardCharsets.UTF_8.toString()));

            // 使用try-with-resources确保流关闭
            try (OutputStream out = response.getOutputStream()) {
                writer.flush(out, true);
                out.flush();
            }

        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("文件名编码失败", e);
        } catch (IOException e) {
            throw new RuntimeException("导出流写入失败", e);
        } catch (Exception e) {
            throw new RuntimeException("导出失败", e);
        } finally {
            writer.close(); // 关闭ExcelWriter，释放POI资源
        }
    }

    @Override
    public void exportRectificationManagement(JSONObject reqJson, HttpServletResponse response) {
        Answer<Object> objectAnswer = this.queryRectificationPage(reqJson);

        if (objectAnswer == null) {
            throw new RuntimeException("分页查询接口返回空，请检查接口实现");
        }
        if (objectAnswer.getCode() != 200) {
            throw new RuntimeException(String.format("分页查询失败：状态码=%d，提示=%s",
                    objectAnswer.getCode(), objectAnswer.getMsg()));
        }

        Page<RectificationInfo> rectificationInfoPage = (Page<RectificationInfo>)objectAnswer.getResult();
        List<RectificationInfo> rectificationInfoList = rectificationInfoPage.getRecords();

        List<Map<String, Object>> rows = rectificationInfoList.stream().map(n -> {
            Map<String, Object> m = new LinkedHashMap<>();
            m.put("工作（项目）名称", n.getTitle());
            m.put("工作（项目）进展及存在问题", n.getProgressAndProblem());
            m.put("当前节点", n.getCurrentNode());
            m.put("牵头领导", n.getLeadLeader());
            m.put("牵头单位", n.getLeadUnit());
            m.put("责任人", n.getPersonLiaible());
            m.put("标签", n.getLabel());
            m.put("当前状态", n.getState());
            return m;
        }).collect(Collectors.toList());

        String fileName = "项目列表导出" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + ".xlsx";
        ExcelWriter writer = ExcelUtil.getBigWriter();
        try {
            LinkedHashMap<String, String> header = new LinkedHashMap<>();
            header.put("工作（项目）名称", "工作（项目）名称");
            header.put("工作（项目）进展及存在问题", "工作（项目）进展及存在问题");
            header.put("当前节点", "当前节点");
            header.put("牵头领导", "牵头领导");
            header.put("牵头单位", "牵头单位");
            header.put("责任人", "责任人");
            header.put("标签", "标签");
            header.put("当前状态", "当前状态");

            header.forEach(writer::addHeaderAlias);
            writer.setOnlyAlias(true);

            String title = "统计分析任务列表";
            writer.merge(header.size() - 1, title);

            Workbook wb = writer.getWorkbook();
            Sheet sheet = writer.getSheet();
            Row r0 = sheet.getRow(0) != null ? sheet.getRow(0) : sheet.createRow(0);
            Cell c0 = r0.getCell(0) != null ? r0.getCell(0) : r0.createCell(0);
            CellStyle titleStyle = wb.createCellStyle();
            titleStyle.setAlignment(org.apache.poi.ss.usermodel.HorizontalAlignment.CENTER);
            titleStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
            org.apache.poi.ss.usermodel.Font titleFont = wb.createFont();
            titleFont.setBold(true);
            titleFont.setFontHeightInPoints((short) 12);
            titleStyle.setFont(titleFont);
            c0.setCellStyle(titleStyle);
            r0.setHeightInPoints(22);

            sheet.createFreezePane(0, 2);

            // 创建自动换行的单元格样式
            CellStyle wrapStyle = wb.createCellStyle();
            wrapStyle.setWrapText(true);


            writer.write(rows, true);

            int i = 0;
            for (String ignored : header.keySet()) {
                writer.setColumnWidth(i, 20);
                // 将自动换行样式应用于第二列
                if (i == 1) {
                    sheet.setDefaultColumnStyle(i, wrapStyle);
                }
                i++;
            }

            // 输出
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + URLEncoder.encode(fileName, java.nio.charset.StandardCharsets.UTF_8.toString()));
            ServletOutputStream out = response.getOutputStream();
            writer.flush(out, true);
            out.flush();
        } catch (Exception e) {
            throw new RuntimeException("导出失败", e);
        } finally {
            writer.close();
        }
    }

    private List<Map<String, Object>> convertStatisticsToExportMap(List<RectificationInfo> list) {
        String projectChildCategoryType = list.get(0).getProjectChildCategoryType();
        projectChildCategoryType = StringUtils.isNotEmpty(projectChildCategoryType)
                ? projectChildCategoryType
                : list.get(0).getProjectChildType();
        projectChildCategoryType = StringUtils.isNotEmpty(projectChildCategoryType)
                ? projectChildCategoryType
                : "默认分类";

        final String finalProjectChildCategoryType = projectChildCategoryType;
        final int[] isMultiSheet = new int[1];

        return list.stream().map(n -> {
            Map<String, Object> m = new LinkedHashMap<>();

            java.util.function.Function<Object, Object> handleNull = value -> {
                if (value == null) {
                    return "";
                }
                if (value instanceof String) {
                    String strValue = (String) value;
                    return StringUtils.isNotEmpty(strValue.trim()) ? strValue : "";
                }
                return value;
            };

            if (finalProjectChildCategoryType.contains("在建重点项目")) {
                m.put("项目名称", handleNull.apply(n.getTitle()));
                m.put("项目简介", handleNull.apply(n.getContent()));
                m.put("实际投资(亿元)", handleNull.apply(n.getAmount()));
                m.put("工作进展及存在问题", handleNull.apply(n.getProgressAndProblem()));
                m.put("当前节点", handleNull.apply(n.getCurrentNode()));
                m.put("牵头领导", handleNull.apply(n.getLeadLeader()));
                m.put("行业主管部门", handleNull.apply(n.getSector()));
                m.put("责任单位", handleNull.apply(n.getLeadUnit()));
                m.put("责任人", handleNull.apply(n.getLeadLeader()));
                m.put("标签", handleNull.apply(n.getLabel()));
                isMultiSheet[0] = 1;
            } else if (finalProjectChildCategoryType.contains("中小学午休“趴睡”改“躺睡")
                    || finalProjectChildCategoryType.contains("打击治理电信网络诈骗")
                    || finalProjectChildCategoryType.contains("开展禁毒整治攻坚")
                    || finalProjectChildCategoryType.contains("车险市场环境专项整治")) {
                m.put("任务", handleNull.apply(n.getTitle()));
                m.put("目标", handleNull.apply(n.getContent()));
                m.put("工作进展及存在问题", handleNull.apply(n.getProgressAndProblem()));
                m.put("牵头领导", handleNull.apply(n.getLeadLeader()));
                m.put("牵头单位", handleNull.apply(n.getDepartmentName()));
                m.put("责任人", handleNull.apply(n.getPersonLiaible()));
                m.put("牵头单位进度", handleNull.apply(n.getProgressAndProblem()));
                m.put("存在的问题", handleNull.apply(n.getRemark()));
                m.put("标签", handleNull.apply(n.getLabel()));
                isMultiSheet[0] = 2;
            } else if (finalProjectChildCategoryType.contains("信访积案化解")) {
                m.put("办件编号", handleNull.apply(n.getTitle()));
                m.put("诉求人姓名", handleNull.apply(n.getPetitioner()));
                m.put("诉求内容", handleNull.apply(n.getContent()));
                m.put("办理进度", handleNull.apply(n.getCompletionAttachments()));
                m.put("当前节点", handleNull.apply(n.getCurrentNode()));
                m.put("牵头领导", handleNull.apply(n.getLeadLeader()));
                m.put("包案责任人", handleNull.apply(n.getPersonLiaible()));
                m.put("责任单位", handleNull.apply(n.getDepartmentName()));
                m.put("办理时限", handleNull.apply(n.getLimitTime()));
                m.put("上访登记日期", handleNull.apply(n.getRecordDate()));
                m.put("诉求分级", handleNull.apply(n.getAppealLevel()));
                m.put("积案年限", handleNull.apply(n.getDuration()));
                m.put("标签", handleNull.apply(n.getLabel()));
                isMultiSheet[0] = 3;
            } else if (finalProjectChildCategoryType.contains("拟开工")){
                m.put("事项名称", handleNull.apply(n.getTitle()));
                m.put("实际投资(亿元)", handleNull.apply(n.getAmount()));
                m.put("工作进展及存在问题", handleNull.apply(n.getProgressAndProblem()));
                m.put("当前节点", handleNull.apply(n.getCurrentNode()));
                m.put("牵头领导", handleNull.apply(n.getLeadLeader()));
                m.put("牵头单位", handleNull.apply(n.getDepartmentName()));
                m.put("责任人", handleNull.apply(n.getPersonLiaible()));
                m.put("标签", handleNull.apply(n.getLabel()));
                isMultiSheet[0] = 5;
            }else {
                m.put("事项名称", handleNull.apply(n.getTitle()));
                m.put("工作进展及存在问题", handleNull.apply(n.getProgressAndProblem()));
                m.put("当前节点", handleNull.apply(n.getCurrentNode()));
                m.put("牵头领导", handleNull.apply(n.getLeadLeader()));
                m.put("牵头单位", handleNull.apply(n.getDepartmentName()));
                m.put("责任人", handleNull.apply(n.getPersonLiaible()));
                m.put("标签", handleNull.apply(n.getLabel()));
                isMultiSheet[0] = 4;
            }

            m.put("isMultiSheet", isMultiSheet[0] != 0 ? isMultiSheet[0] : 4);
            return m;
        }).collect(Collectors.toList());
    }
    private LinkedHashMap<String, String> createStatisticsHeaderMap(int isMultiSheet) {
        LinkedHashMap<String, String> header = new LinkedHashMap<>();
        if (isMultiSheet ==1 ) {
            header.put("项目名称", "项目名称");
            header.put("项目简介", "项目简介");
            header.put("实际投资(亿元)", "实际投资(亿元)");
            header.put("工作进展及存在问题", "工作进展及存在问题");
            header.put("当前节点", "当前节点");
            header.put("牵头领导", "牵头领导");
            header.put("行业主管部门", "行业主管部门");
            header.put("责任单位", "责任单位");
            header.put("责任人", "责任人");
            header.put("标签", "标签");
        } else if(isMultiSheet==2){
            header.put("任务", "任务");
            header.put("目标", "目标");
            header.put("工作进展及存在问题", "工作进展及存在问题");
            header.put("牵头领导", "牵头领导");
            header.put("牵头单位", "牵头单位");
            header.put("责任人", "责任人");
            header.put("牵头单位进度", "牵头单位进度");
            header.put("存在的问题", "存在的问题");
            header.put("标签", "标签");
        }else if(isMultiSheet==3){
            header.put("办件编号", "办件编号");
            header.put("诉求人姓名", "诉求人姓名");
            header.put("诉求内容", "诉求内容");
            header.put("办理进度", "办理进度");
            header.put("当前节点", "当前节点");
            header.put("牵头领导", "牵头领导");
            header.put("包案责任人", "包案责任人");
            header.put("责任单位", "责任单位");
            header.put("办理时限", "办理时限");
            header.put("上访登记日期", "上访登记日期");
            header.put("诉求分级", "诉求分级");
            header.put("积案年限", "积案年限");
            header.put("标签", "标签");
        }else if (isMultiSheet==4){
            header.put("事项名称", "事项名称");
            header.put("工作进展及存在问题", "工作进展及存在问题");
            header.put("当前节点", "当前节点");
            header.put("牵头领导", "牵头领导");
            header.put("牵头单位", "牵头单位");
            header.put("责任人", "责任人");
            header.put("标签", "标签");
        }else if (isMultiSheet==5){
            header.put("事项名称", "事项名称");
            header.put("实际投资(亿元)", "实际投资(亿元)");
            header.put("工作进展及存在问题", "工作进展及存在问题");
            header.put("当前节点", "当前节点");
            header.put("牵头领导", "牵头领导");
            header.put("牵头单位", "牵头单位");
            header.put("责任人", "责任人");
            header.put("标签", "标签");
        }
        return header;
    }



    @Override
    public void exportDeadlineByMainId(DeadlineNodeOutput req, HttpServletResponse response) {
        RectificationInfo rectificationInfo = rectificationInfoMapper.selectById(req.getMainId());

        List<TicketProblemInfo> ticketProblemInfoList = ticketProblemInfoMapper.selectList(
                new LambdaQueryWrapper<TicketProblemInfo>()
                        .eq(TicketProblemInfo::getTask2Id, rectificationInfo.getTask2Id()));

        // 修复空指针问题：将&&改为||
        if (ticketProblemInfoList == null || ticketProblemInfoList.isEmpty()) {
            LambdaQueryWrapper<DeadlineNode> qw = Wrappers.lambdaQuery(DeadlineNode.class)
                    .eq(req.getMainId() != null, DeadlineNode::getRectificationId, req.getMainId())
                    .orderByAsc(DeadlineNode::getSort, DeadlineNode::getId);

            List<DeadlineNode> list = deadlineNodeMapper.selectList(qw);
            list = filiterNode(rectificationInfo, list);

            // 处理节点数据
            processDeadlineNodes(list);
            List<Map<String, Object>> rows = new ArrayList<>();
            if (StringUtils.isBlank(rectificationInfo.getProjectChildCategoryType())){
                rectificationInfo.setProjectChildCategoryType(rectificationInfo.getProjectChildType());
            }
            if (rectificationInfo.getProjectChildCategoryType().contains("拟开工重点项目")||
                    rectificationInfo.getProjectChildCategoryType().contains("谋划推进重大项目")||
                    rectificationInfo.getProjectChildCategoryType().contains("签约未开工重点项目")||
                    rectificationInfo.getProjectChildCategoryType().contains("招商签约未开工重点项目")||
                    rectificationInfo.getProjectChildCategoryType().contains("谋划重大项目")) {
                rows=  convertToExportMap(list, 2);
            }else if (rectificationInfo.getProjectChildCategoryType().contains("在建重点项目")){
                rows=  convertToExportMap(list, 3);
            }else if (rectificationInfo.getProjectChildCategoryType().contains("河湖长制")) {
                rows = convertToExportMap(list, 5);
            }else if (rectificationInfo.getProjectChildCategoryType().contains("林长制")){
                rows = convertToExportMap(list, 6);
            }else if (rectificationInfo.getProjectChildCategoryType().contains("打击治理电信网络诈骗")||
                    rectificationInfo.getProjectChildCategoryType().contains("开展禁毒整治攻坚")||
                    rectificationInfo.getProjectChildCategoryType().contains("车险市场环境专项整治")||
                    rectificationInfo.getProjectChildCategoryType().contains("省市十大民生项目滞后事项")){
                rows = convertToExportMap(list, 7);
            }else {
                rows=  convertToExportMap(list, 4);
            }



            String fileName = "项目节点导出_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + ".xlsx";
            ExcelWriter writer = ExcelUtil.getBigWriter();
            try {
                LinkedHashMap<String, String> header = new LinkedHashMap<>();

                if (rectificationInfo.getProjectChildCategoryType().contains("拟开工重点项目")||
                        rectificationInfo.getProjectChildCategoryType().contains("谋划推进重大项目")||
                        rectificationInfo.getProjectChildCategoryType().contains("签约未开工重点项目")||
                        rectificationInfo.getProjectChildCategoryType().contains("招商签约未开工重点项目")||
                        rectificationInfo.getProjectChildCategoryType().contains("谋划重大项目")) {
                    header = createHeaderMap(2);
                }else if (rectificationInfo.getProjectChildCategoryType().contains("在建重点项目")){
                    header = createHeaderMap(3);
                }else if (rectificationInfo.getProjectChildCategoryType().contains("河湖长制")) {
                    header = createHeaderMap(5);
                }else if (rectificationInfo.getProjectChildCategoryType().contains("林长制")){
                    header = createHeaderMap(6);
                }else if (rectificationInfo.getProjectChildCategoryType().contains("打击治理电信网络诈骗")||
                        rectificationInfo.getProjectChildCategoryType().contains("开展禁毒整治攻坚")||
                        rectificationInfo.getProjectChildCategoryType().contains("车险市场环境专项整治")||
                        rectificationInfo.getProjectChildCategoryType().contains("省市十大民生项目滞后事项")){
                    header = createHeaderMap(7);
                }else {
                    header = createHeaderMap(4);
                }

                exportToExcel(writer, header, rows, rectificationInfo.getTitle(), null);

                // 输出
                writeResponse(response, writer, fileName);
            } catch (Exception e) {
                throw new RuntimeException("导出失败", e);
            } finally {
                writer.close();
            }
        } else {
            Map<String, List<DeadlineNode>> dMap = new HashMap<>();
            List<String> departmentNameList = ticketProblemInfoList.stream()
                    .map(TicketProblemInfo::getProcessHandlerDepartment)
                    .distinct()
                    .filter(StrUtil::isNotBlank)
                    .collect(Collectors.toList());

            for (String departmentName : departmentNameList) {
                List<TicketProblemInfo> collect = ticketProblemInfoList.stream()
                        .filter(tp -> departmentName.equals(tp.getProcessHandlerDepartment()))
                        .collect(Collectors.toList());

                if (!collect.isEmpty()) {
                    Integer mainId = collect.get(0).getId();
                    List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(
                            new LambdaQueryWrapper<DeadlineNode>()
                                    .eq(DeadlineNode::getMainId, mainId));

                    for (DeadlineNode node : deadlineNodeList) {
                        node.setDepartmentName(departmentName);
                    }

                    // 处理节点数据
                    processDeadlineNodes(deadlineNodeList);
                    dMap.put(departmentName, deadlineNodeList);
                }
            }

            String fileName = "项目节点导出_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + ".xlsx";
            ExcelWriter writer = ExcelUtil.getBigWriter();
            try {
                // 定义表头
                LinkedHashMap<String, String> header = new LinkedHashMap<>();
                if (rectificationInfo.getProjectChildType().contains("河湖长制")) {
                    header = createHeaderMap(5);
                }else if (rectificationInfo.getProjectChildType().contains("林长制")){
                    header = createHeaderMap(6);
                }else if (rectificationInfo.getProjectChildType().contains("打击治理电信网络诈骗")||
                        rectificationInfo.getProjectChildType().contains("开展禁毒整治攻坚")||
                        rectificationInfo.getProjectChildType().contains("车险市场环境专项整治")||
                        rectificationInfo.getProjectChildType().contains("省市十大民生项目滞后事项")){
                    header = createHeaderMap(8);
                }else {
                    header = createHeaderMap(1);
                }

                // 为每个部门创建一个sheet
                for (int sheetIndex = 0; sheetIndex < departmentNameList.size(); sheetIndex++) {
                    String departmentName = departmentNameList.get(sheetIndex);
                    if (!dMap.containsKey(departmentName)) {
                        continue;
                    }

                    // 如果不是第一个sheet，创建新sheet
                    if (sheetIndex > 0) {
                        writer.setSheet(departmentName);
                    } else {
                        // 第一个sheet使用默认sheet，设置名称
                        writer.renameSheet(departmentName);
                    }

                    // 获取当前部门的数据
                    List<DeadlineNode> deadlineNodeList = dMap.get(departmentName);
                    List<Map<String, Object>> rows = new ArrayList<>();
                    // 转换为导出格式

                    if (rectificationInfo.getProjectChildType().contains("河湖长制")) {
                        rows = convertToExportMap(deadlineNodeList, 5);
                    }else if (rectificationInfo.getProjectChildType().contains("林长制")){
                        rows = convertToExportMap(deadlineNodeList, 6);
                    }else if (rectificationInfo.getProjectChildType().contains("打击治理电信网络诈骗")||
                            rectificationInfo.getProjectChildType().contains("开展禁毒整治攻坚")||
                            rectificationInfo.getProjectChildType().contains("车险市场环境专项整治")||
                            rectificationInfo.getProjectChildType().contains("省市十大民生项目滞后事项")){
                        rows = convertToExportMap(deadlineNodeList, 8);
                    }else {
                        rows = convertToExportMap(deadlineNodeList, 1);
                    }


                    // 导出到Excel
                    exportToExcel(writer, header, rows, rectificationInfo.getTitle(), departmentName);
                }

                // 输出文件
                writeResponse(response, writer, fileName);
            } catch (Exception e) {
                throw new RuntimeException("导出失败", e);
            } finally {
                writer.close();
            }
        }
    }

    /**
     * 处理节点数据，计算时限等信息
     */
    private void processDeadlineNodes(List<DeadlineNode> list) {
        for (int i = 0; i < list.size(); i++) {
            if (i == 0) {
                Date deadline = list.get(i).getDeadline();
                Date endTime = list.get(i).getEndTime();
                if (deadline != null && endTime != null) {
                    long diffInMillies = endTime.getTime() - deadline.getTime();
                    if (diffInMillies < 0) {
                        diffInMillies = 0;
                    } else {
                        diffInMillies = diffInMillies / 1000 / 60 / 60 / 24;
                    }
                    list.get(i).setDeadlineType(String.valueOf(diffInMillies));
                    list.get(i).setDuration(0);
                }
            } else {
                Date endTime = list.get(i).getDeadline();
                Date deadline = list.get(0).getDeadline();
                if (deadline != null && endTime != null) {
                    long diffInMillies = endTime.getTime() - deadline.getTime();
                    if (diffInMillies < 0) {
                        diffInMillies = 0;
                    } else {
                        diffInMillies = diffInMillies / 1000 / 60 / 60 / 24;
                    }
                    list.get(i).setDeadlineType(String.valueOf(diffInMillies));
                }
                if (list.get(i).getEndTime()!=null && list.get(0).getEndTime()!=null){
                    Date endTimeE = list.get(i).getEndTime();
                    Date deadlineE = list.get(0).getEndTime();
                    if (deadline != null && endTimeE != null) {
                        long diffInMillies = endTimeE.getTime() - deadlineE.getTime();
                        if (diffInMillies < 0) {
                            diffInMillies = 0;
                        } else {
                            diffInMillies = diffInMillies / 1000 / 60 / 60 / 24;
                        }
                        list.get(i).setDuration((int)diffInMillies);
                    }
                }else {
                    list.get(i).setDuration(null);
                }
            }
            if (list.get(i).getEndTime() == null) {
                Date date = new Date();
                if (list.get(i).getDeadline() != null && date.after(list.get(i).getDeadline())) {
                    list.get(i).setProgress("未完成");
                }else {
                    list.get(i).setProgress(null);
                }
            }
        }
    }

    /**
     * 创建表头映射
     */
    private LinkedHashMap<String, String> createHeaderMap(int isMultiSheet) {
        LinkedHashMap<String, String> header = new LinkedHashMap<>();
        if (isMultiSheet==1) {
            header.put("进度安排", "进度安排");
            header.put("责任人", "责任人");
            header.put("任务", "任务");
            header.put("要求完成时间", "要求完成时间");
            header.put("整改后时间", "整改后时间");
            header.put("状态", "状态");
            header.put("实际完成时间", "实际完成时间");
            header.put("备注", "备注");
        } else if(isMultiSheet==2){
            header.put("节点名称", "节点名称");
            header.put("责任单位", "责任单位");
            header.put("责任人", "责任人");
            header.put("要求完成时限(天)", "要求完成时限(天)");
            header.put("实际用时", "实际用时");
            header.put("要求完成时间", "要求完成时间");
            header.put("整改后时间", "整改后时间");
            header.put("状态", "状态");
            header.put("实际完成时间", "实际完成时间");
            header.put("备注", "备注");
        }else if(isMultiSheet==3){
            header.put("节点名称", "节点名称");
            header.put("目标任务", "目标任务");
            header.put("责任单位", "责任单位");
            header.put("责任人", "责任人");
            header.put("实际用时", "实际用时");
            header.put("要求完成时间", "要求完成时间");
            header.put("整改后时间", "整改后时间");
            header.put("状态", "状态");
            header.put("实际完成时间", "实际完成时间");
            header.put("备注", "备注");
        }else if(isMultiSheet==4){
            header.put("节点名称", "节点名称");
            header.put("责任单位", "责任单位");
            header.put("责任人", "责任人");
            header.put("实际用时", "实际用时");
            header.put("要求完成时间", "要求完成时间");
            header.put("整改后时间", "整改后时间");
            header.put("状态", "状态");
            header.put("实际完成时间", "实际完成时间");
            header.put("备注", "备注");
        }else if(isMultiSheet==5){
            header.put("进度安排", "进度安排");
            header.put("责任人", "责任人");
            header.put("任务目标", "任务目标");
            header.put("完成时限", "完成时限");
            header.put("整改后时间", "整改后时间");
            header.put("工作状态", "工作状态");
            header.put("实际完成时间", "实际完成时间");
            header.put("备注", "备注");
            header.put("更新时间", "更新时间");
        }if(isMultiSheet==6){
            header.put("进度安排", "进度安排");
            header.put("责任人", "责任人");
            header.put("要求完成时间", "要求完成时间");
            header.put("整改后时间", "整改后时间");
            header.put("任务", "任务");
            header.put("实际完成时间", "实际完成时间");
            header.put("备注", "备注");
            header.put("更新时间", "更新时间");
        }else  if(isMultiSheet==7){
            header.put("节点名称", "节点名称");
            header.put("要求", "要求");
            header.put("责任人", "责任人");
            header.put("要求完成时间", "要求完成时间");
            header.put("整改后时间", "整改后时间");
            header.put("实际完成时间", "实际完成时间");
            header.put("当前进展", "当前进展");
            header.put("存在问题", "存在问题");
        }else {
            header.put("进度安排", "进度安排");
            header.put("责任人", "责任人");
            header.put("任务", "任务");
            header.put("要求完成时间", "要求完成时间");
            header.put("整改后时间", "整改后时间");
            header.put("完成状态", "完成状态");
            header.put("实际完成时间", "实际完成时间");
            header.put("备注", "备注");
        }
        return header;
    }

    /**
     * 转换为导出用的Map列表
     */
    private List<Map<String, Object>> convertToExportMap(List<DeadlineNode> list, int isMultiSheet) {
        return list.stream().map(n -> {
            Map<String, Object> m = new LinkedHashMap<>();
            if (isMultiSheet==1) {
                m.put("进度安排", n.getName());
                m.put("责任人", n.getDescription());
                m.put("任务", n.getContent());
                m.put("要求完成时间", fmt(n.getDeadline()));
                m.put("整改后时间", fmt(n.getDeadlineRectificationPeriod()));
                m.put("状态", n.getProgress());
                m.put("实际完成时间", fmt(n.getEndTime()));
                m.put("备注", n.getRemark());
            } else if (isMultiSheet==2){
                m.put("节点名称", n.getName());
                m.put("责任单位", n.getDepartmentName());
                m.put("责任人", n.getDescription());
                m.put("要求完成时限(天)", n.getDeadlineType());
                m.put("实际用时", n.getDuration());
                m.put("要求完成时间", fmt(n.getDeadline()));
                m.put("整改后时间", fmt(n.getDeadlineRectificationPeriod()));
                m.put("状态", n.getProgress());
                m.put("实际完成时间", fmt(n.getEndTime()));
                m.put("备注", n.getRemark());
            }else if (isMultiSheet==3){
                m.put("节点名称", n.getName());
                m.put("目标任务", n.getContent());
                m.put("责任单位", n.getDepartmentName());
                m.put("责任人", n.getDescription());
                m.put("实际用时", n.getDuration());
                m.put("要求完成时间", fmt(n.getDeadline()));
                m.put("整改后时间", fmt(n.getDeadlineRectificationPeriod()));
                m.put("状态", n.getProgress());
                m.put("实际完成时间", fmt(n.getEndTime()));
                m.put("备注", n.getRemark());
            }else if (isMultiSheet==4){
                m.put("节点名称", n.getName());
                m.put("责任单位", n.getDepartmentName());
                m.put("责任人", n.getDescription());
                m.put("实际用时", n.getDuration());
                m.put("要求完成时间", fmt(n.getDeadline()));
                m.put("整改后时间", fmt(n.getDeadlineRectificationPeriod()));
                m.put("状态", n.getProgress());
                m.put("实际完成时间", fmt(n.getEndTime()));
                m.put("备注", n.getRemark());
            }else if (isMultiSheet==5) {
                m.put("进度安排", n.getName());
                m.put("责任人", n.getDescription());
                m.put("任务目标", n.getContent());
                m.put("完成时限", fmt(n.getDeadline()));
                m.put("整改后时间", fmt(n.getDeadlineRectificationPeriod()));
                m.put("工作状态", n.getProgress());
                m.put("实际完成时间", fmt(n.getEndTime()));
                m.put("备注", n.getRemark());
                m.put("更新时间", fmt(n.getUpdateTime()));
            }else if (isMultiSheet==6) {
                m.put("进度安排", n.getName());
                m.put("责任人", n.getDescription());;
                m.put("要求完成时间", fmt(n.getDeadline()));
                m.put("整改后时间", fmt(n.getDeadlineRectificationPeriod()));
                m.put("任务", n.getContent());
                m.put("实际完成时间", fmt(n.getEndTime()));
                m.put("备注", n.getRemark());
                m.put("更新时间", fmt(n.getUpdateTime()));
            } else if(isMultiSheet==7){
                m.put("节点名称", n.getName());
                m.put("要求", n.getContent());
                m.put("责任人", n.getDescription());;
                m.put("要求完成时间", fmt(n.getDeadline()));
                m.put("整改后时间", fmt(n.getDeadlineRectificationPeriod()));
                m.put("实际完成时间",fmt(n.getEndTime()));
                m.put("当前进展", n.getProgress());
                m.put("存在问题", n.getRemark());
            }else{
                m.put("进度安排", n.getName());
                m.put("责任人", n.getDescription());;
                m.put("任务", n.getContent());
                m.put("要求完成时间", fmt(n.getDeadline()));
                m.put("整改后时间", fmt(n.getDeadlineRectificationPeriod()));
                m.put("完成状态",n.getProgress());
                m.put("实际完成时间",fmt(n.getEndTime()));
                m.put("备注", n.getRemark());
            }
            return m;
        }).collect(Collectors.toList());
    }

    /**
     * 设置Excel样式并写入数据
     */
    private void exportToExcel(ExcelWriter writer, LinkedHashMap<String, String> header,
                               List<Map<String, Object>> rows, String title, String departmentName) {
        // 设置表头别名
        header.forEach(writer::addHeaderAlias);
        writer.setOnlyAlias(true);

        // 添加标题
        String fullTitle = title;
        if (StrUtil.isNotBlank(departmentName)) {
            fullTitle += " - " + departmentName;
        }
        writer.merge(header.size() - 1, fullTitle);

        Workbook wb = writer.getWorkbook();
        Sheet sheet = writer.getSheet();

        // 设置标题样式
        setTitleStyle(wb, sheet);

        // 冻结窗格
//        sheet.createFreezePane(0, 2);
        List<Map<String, String>> headerlist = new ArrayList<>();
        headerlist.add(header);
        writer.write(headerlist, false);

        writer.setCurrentRow(2);
        // 写入数据
        writer.write(rows, false);

        // 设置列宽
        int i = 0;
        for (String ignored : header.keySet()) {
            writer.setColumnWidth(i++, 20);
        }

        // 设置数据单元格样式
        setDataCellStyle(wb, sheet, header.size());
    }

    /**
     * 设置标题样式
     */
    private void setTitleStyle(Workbook wb, Sheet sheet) {
        Row r0 = sheet.getRow(0) != null ? sheet.getRow(0) : sheet.createRow(0);
        Cell c0 = r0.getCell(0) != null ? r0.getCell(0) : r0.createCell(0);
        CellStyle titleStyle = wb.createCellStyle();
        titleStyle.setAlignment(org.apache.poi.ss.usermodel.HorizontalAlignment.CENTER);
        titleStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
        titleStyle.setWrapText(true);
        org.apache.poi.ss.usermodel.Font titleFont = wb.createFont();
        titleFont.setBold(true);
        titleFont.setFontHeightInPoints((short) 12);
        titleStyle.setFont(titleFont);
        c0.setCellStyle(titleStyle);
        r0.setHeight((short) -1);
    }

    /**
     * 设置数据单元格样式
     */
    private void setDataCellStyle(Workbook wb, Sheet sheet, int columnCount) {
        CellStyle dataCellStyle = wb.createCellStyle();
        dataCellStyle.setWrapText(true);
        dataCellStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
        dataCellStyle.setBorderTop(org.apache.poi.ss.usermodel.BorderStyle.THIN);
        dataCellStyle.setBorderBottom(org.apache.poi.ss.usermodel.BorderStyle.THIN);
        dataCellStyle.setBorderLeft(org.apache.poi.ss.usermodel.BorderStyle.THIN);
        dataCellStyle.setBorderRight(org.apache.poi.ss.usermodel.BorderStyle.THIN);

        for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) {
            Row dataRow = sheet.getRow(rowNum) != null ? sheet.getRow(rowNum) : sheet.createRow(rowNum);
            for (int colNum = 0; colNum < columnCount; colNum++) {
                Cell dataCell = dataRow.getCell(colNum) != null ? dataRow.getCell(colNum) : dataRow.createCell(colNum);
                dataCell.setCellStyle(dataCellStyle);
            }
            dataRow.setHeight((short) -1);
        }
    }

    /**
     * 写入响应
     */
    private void writeResponse(HttpServletResponse response, ExcelWriter writer, String fileName) throws IOException {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition",
                "attachment;filename=" + URLEncoder.encode(fileName, java.nio.charset.StandardCharsets.UTF_8.toString()));
        ServletOutputStream out = response.getOutputStream();
        writer.flush(out, true);
        out.flush();
    }

    @Override
    public Answer<Object> groupByTitle() {
        List<RectificationInfo> rectificationInfos = fetchAllRectification("");
        List<String> title = new ArrayList<>();
        for (RectificationInfo rectificationInfo : rectificationInfos) {
            title.add(rectificationInfo.getTitle());
        }
        return Answer.ok(title);
    }

    @Override
    public Answer<Object> groupByPersonLiaible() {
        List<RectificationInfo> rectificationInfos = fetchAllRectification("");
        // 1. 定义所有需要处理的分隔符（包含中英文符号：, / . ; ，、）
        // 正则说明：[] 表示匹配其中任意一个字符，\\ 转义特殊符号（如 . 需转义为 \\.）
        String separatorRegex = "[,/;，、\\\\.]"; // 顺序：英文逗号、斜杠、分号、中文逗号、中文顿号、反斜杠、英文点
        Pattern separatorPattern = Pattern.compile(separatorRegex);
        // 2. 用HashSet直接存储，避免先存List再转Set（减少一次集合转换，更高效）
        Set<String> personLiableSet = new HashSet<>();
        personLiableSet.addAll(Arrays.asList(
                "付兴伟","郑斌","王振江","李剑"
        ));

        List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(new LambdaQueryWrapper<>());
        Map<Integer,List<DeadlineNode>> deadlineNodeMap = new HashMap<>();
        if (deadlineNodeList!=null&&!deadlineNodeList.isEmpty()){
            if (deadlineNodeList != null && !deadlineNodeList.isEmpty()) {
                deadlineNodeMap = deadlineNodeList.stream().filter(node -> node.getRectificationId() != null)
                        .collect(Collectors.groupingBy(DeadlineNode::getRectificationId));
            }
        }

        for (RectificationInfo rectificationInfo : rectificationInfos) {
            String personLiable = rectificationInfo.getPersonLiaible();
            // 先过滤空值/空字符串，避免后续处理异常
            if (StringUtils.isBlank(personLiable)) {
                continue;
            }

            // 3. 按正则分割字符串（处理所有分隔符）
            // split("\\s+") 额外处理分割后可能残留的空格（如 "张三, 李四" 分割后去空格）
            String[] personParts = separatorPattern.split(personLiable);
            for (String part : personParts) {
                // 去除每个责任人的前后空格（避免 " 张三" 和 "张三" 被判定为不同值）
                String trimmedPart = StringUtils.trim(part);
                // 过滤分割后可能产生的空字符串（如 "张三,,李四" 分割后会有空元素）
                if (StringUtils.isNotBlank(trimmedPart)) {
                    personLiableSet.add(trimmedPart);
                }
            }

            List<DeadlineNode> deadlineNodes = deadlineNodeMap.get(rectificationInfo.getId());
            if (deadlineNodes!=null&&!deadlineNodes.isEmpty()) {
                deadlineNodes = filiterNode(rectificationInfo, deadlineNodes);
            }
            if (deadlineNodes!=null&&!deadlineNodes.isEmpty()) {
                for (DeadlineNode deadlineNode : deadlineNodes) {
                    String description = deadlineNode.getDescription();
                    if (StringUtils.isBlank(description)) {
                        continue;
                    }
                    String[] descriptions = separatorPattern.split(description);
                    for (String part : descriptions) {
                        // 去除每个责任人的前后空格（避免 " 张三" 和 "张三" 被判定为不同值）
                        String trimmedPart = StringUtils.trim(part);
                        // 过滤分割后可能产生的空字符串（如 "张三,,李四" 分割后会有空元素）
                        if (StringUtils.isNotBlank(trimmedPart)) {
                            if(trimmedPart.length()<5){
                                personLiableSet.add(trimmedPart);
                            }
                        }
                    }
                }
            }
        }

        // 4. 将Set转为List返回（若需保持固定顺序，可改用 LinkedHashSet）
        List<String> newList = new ArrayList<>(personLiableSet);


        return Answer.ok(newList);
    }

    @Override
    public Answer<Object> groupByDepartment() {
        LambdaQueryWrapper<RectificationInfo> rectificationInfoQueryWrapper = new LambdaQueryWrapper<>();
        rectificationInfoQueryWrapper.ne(RectificationInfo::getState, "已关闭").ne(RectificationInfo::getState, "已销号").ne(RectificationInfo::getState, "已销号");
        List<RectificationInfo> rectificationInfos = rectificationInfoMapper.selectList(rectificationInfoQueryWrapper);

        // 1. 定义部门名称可能的分隔符（中英文逗号、顿号、斜杠、分号等，与责任人逻辑一致）
        String separatorRegex = "[,/;，、\\\\.]"; // 匹配：, / ; ，、 \ .
        Pattern separatorPattern = Pattern.compile(separatorRegex);

        // 2. 用HashSet直接去重，避免多次集合转换（高效）
        Set<String> departmentSet = new LinkedHashSet<>(); // 用LinkedHashSet可保持部门首次出现顺序

        for (RectificationInfo rectificationInfo : rectificationInfos) {
            String deptStr = rectificationInfo.getDepartmentName();
            // 过滤空值/空字符串/纯空格，避免无效数据
            if (StringUtils.isBlank(deptStr)) {
                continue;
            }

            // 3. 按多分隔符分割部门字符串，处理每个部门名称
            String[] deptParts = separatorPattern.split(deptStr);
            for (String deptPart : deptParts) {
                // 去除部门名称前后空格（避免 " 技术部" 和 "技术部" 被判定为不同）
                String trimmedDept = StringUtils.trim(deptPart);
                // 过滤分割后产生的空字符串（如 "技术部,,财务部" 分割后会有空元素）
                if (StringUtils.isNotBlank(trimmedDept)) {
                    departmentSet.add(trimmedDept);
                }
            }
        }
        // 4. 转为List返回（若无需顺序，LinkedHashSet可改为普通HashSet）
        List<String> newList = new ArrayList<>(departmentSet);
        return Answer.ok(newList);
    }

    @Override
    public Answer<Object> groupByLeadLeader() {
        List<RectificationInfo> rectificationInfos= fetchAllRectification("");
        // 1. 定义牵头领导字段可能的所有分隔符（中英文符号全覆盖，与责任人/部门逻辑一致）
        // 匹配：英文逗号(,)、斜杠(/)、分号(;)、中文逗号(，)、中文顿号(、)、反斜杠(\)、英文点(.)
        String separatorRegex = "[,/;，、\\\\.]";
        Pattern separatorPattern = Pattern.compile(separatorRegex);

        // 2. 用LinkedHashSet：既去重，又保留牵头领导首次出现的顺序（更符合业务展示习惯）
        Set<String> leadLeaderSet = new LinkedHashSet<>();

        leadLeaderSet.addAll(Arrays.asList(
                "马梁", "汪国兵", "查俊", "郝爱芳", "高维鹏",
                "陈正红", "胡广", "胡安元", "赵斌", "刘君峰",
                "孙迎松", "肖红娟", "程志文", "刘涛","贺剑平","胡晓燕","王开伟",
                "李华桥","童承志"
        ));
        for (RectificationInfo rectificationInfo : rectificationInfos) {
            String leaderStr = rectificationInfo.getLeadLeader();
            // 先过滤空值/空字符串/纯空格，避免无效处理和空指针
            if (StringUtils.isBlank(leaderStr)) {
                continue;
            }

            // 3. 按多分隔符分割字符串，处理每个牵头领导名称
            String[] leaderParts = separatorPattern.split(leaderStr);
            for (String part : leaderParts) {
                // 去除每个名称的前后空格（避免 " 张总" 和 "张总" 被判定为不同）
                String trimmedLeader = StringUtils.trim(part);
                // 过滤分割后产生的空字符串（如 "李总,,王总" 分割后会有空元素，需排除）
                if (StringUtils.isNotBlank(trimmedLeader)) {
                    leadLeaderSet.add(trimmedLeader);
                }
            }
        }

        // 4. 转为List返回（满足接口返回格式要求）
        List<String> newList = new ArrayList<>(leadLeaderSet);
        return Answer.ok(newList);
    }

    @Override
    public Answer<Object> groupByCurrentNode() {
        LambdaQueryWrapper<RectificationInfo> rectificationInfoQueryWrapper = new LambdaQueryWrapper<>();
        rectificationInfoQueryWrapper.ne(RectificationInfo::getState, "已关闭").ne(RectificationInfo::getState, "已销号").ne(RectificationInfo::getState, "已销号");
        List<RectificationInfo> rectificationInfos = rectificationInfoMapper.selectList(rectificationInfoQueryWrapper);
        List<String> currentNode = new ArrayList<>();
        for (RectificationInfo rectificationInfo : rectificationInfos) {
            currentNode.add(rectificationInfo.getCurrentNode());
        }
        HashSet<String> set = new HashSet<>(currentNode);
        // 将去重后的元素转回ArrayList
        List<String> newList = new ArrayList<>(set);
        return Answer.ok(newList);
    }

    @Override
    public Answer<Object> getRectificationInfoByLike(RectificationInfo rectificationInfo) {
        LambdaQueryWrapper<RectificationInfo> rectificationInfoQueryWrapper = new LambdaQueryWrapper<>();
        IPage<RectificationInfo> page = new Page<>(rectificationInfo.getPageNum(), rectificationInfo.getPageSize());
        if (StrUtil.isNotBlank(rectificationInfo.getTitle())) {
            rectificationInfoQueryWrapper.like(RectificationInfo::getTitle, rectificationInfo.getTitle());
        }
        rectificationInfoQueryWrapper.ne(RectificationInfo::getState, "已关闭").ne(RectificationInfo::getState, "已销号").ne(RectificationInfo::getState, "已销号");
        if (StrUtil.isNotBlank(rectificationInfo.getPersonLiaible())) {
            rectificationInfoQueryWrapper.like(RectificationInfo::getPersonLiaible, rectificationInfo.getPersonLiaible());
        }
        if (StrUtil.isNotBlank(rectificationInfo.getDepartmentName())) {
            rectificationInfoQueryWrapper.like(RectificationInfo::getDepartmentName, rectificationInfo.getDepartmentName());
        }
        if (StrUtil.isNotBlank(rectificationInfo.getLeadLeader())) {
            rectificationInfoQueryWrapper.like(RectificationInfo::getLeadLeader, rectificationInfo.getLeadLeader());
        }
        if (StrUtil.isNotBlank(rectificationInfo.getCurrentNode())) {
            rectificationInfoQueryWrapper.like(RectificationInfo::getCurrentNode, rectificationInfo.getCurrentNode());
        }
        IPage<RectificationInfo> rectificationInfoIPage = rectificationInfoMapper.selectPage(page, rectificationInfoQueryWrapper);
        return Answer.ok(rectificationInfoIPage);
    }

    private static LocalDateTime toLdt(DateTime dt) {
        return dt == null ? null : dt.toLocalDateTime();
    }

    private static final DateTimeFormatter DF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");

    private String fmt(Date t) {
        return t == null ? "" : DF.format(t.toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDateTime());
    }

    @Transactional
    public String nodeList(RectificationInfo rectificationInfo, DateTime deadline) {
        List<DeadlineNode> deadlineNodes = new ArrayList<>();
        //生成项目（签约）
        DeadlineNode deadlineNode = new DeadlineNode();
        deadlineNode.setMainId(rectificationInfo.getId());//mainId
//        deadlineNode.setDeadline(deadline);
        deadlineNode.setSort(0);
        deadlineNode.setCreateTime(new Date());
        deadlineNode.setName("生成项目（签约）");
//        deadlineNode.setDeadlineType("责任人");
        deadlineNode.setTypeName("整改");
        deadlineNode.setIsEnd("0");
        deadlineNode.setRectificationId(rectificationInfo.getId());
        deadlineNode.setWarnLevel("0");
//        deadlineNode.setDepartmentName("部门");
//        deadlineNode.setDeadlineType("0d");
        deadlineNodes.add(deadlineNode);
        //纳入上级规划/纳入年度投资计划/完成项目建议书批复，启动项目实施
        DeadlineNode deadlineNode1 = new DeadlineNode();
        deadlineNode1.setMainId(rectificationInfo.getId());//mainId
        deadlineNode1.setDeadline(deadline);
        deadlineNode1.setSort(1);
        deadlineNode1.setCreateTime(new Date());
        deadlineNode1.setName("纳入上级规划/纳入年度投资计划/完成项目建议书批复，启动项目实施");
        deadlineNode1.setDescription(responsiblePersonsService.locatePerson(deadlineNode1.getName(), rectificationInfo.getLeadUnit()));
        deadlineNode1.setTypeName("整改");
        deadlineNode1.setIsEnd("0");
        deadlineNode1.setWarnLevel("0");
        deadlineNode1.setRectificationId(rectificationInfo.getId());
        deadlineNode1.setDepartmentName("发改部门");
        deadlineNode1.setDeadlineType("0d");
        deadlineNode1.setState("1");
        deadlineNodes.add(deadlineNode1);
        //完成可行性研究报告批复
        DeadlineNode deadlineNode2 = new DeadlineNode();
        deadlineNode2.setMainId(rectificationInfo.getId());//mainId
        deadlineNode2.setDeadline(calculateFutureDate(deadline, 30));
        deadlineNode2.setSort(2);
        deadlineNode2.setRectificationId(rectificationInfo.getId());
        deadlineNode2.setCreateTime(new Date());
        deadlineNode2.setName("完成可行性研究报告批复");
        deadlineNode2.setDescription(responsiblePersonsService.locatePerson(deadlineNode2.getName(), rectificationInfo.getLeadUnit()));
        deadlineNode2.setTypeName("整改");
        deadlineNode2.setIsEnd("0");
        deadlineNode2.setWarnLevel("0");
        deadlineNode2.setDepartmentName("发改部门");
        deadlineNode2.setDeadlineType("30d");
        deadlineNode2.setState("1");
        deadlineNodes.add(deadlineNode2);
        //完成规划选址
        DeadlineNode deadlineNode3 = new DeadlineNode();
        deadlineNode3.setMainId(rectificationInfo.getId());//mainId
//        deadlineNode3.setDeadline(deadline);
        deadlineNode3.setSort(3);
        deadlineNode3.setCreateTime(new Date());
        deadlineNode3.setRectificationId(rectificationInfo.getId());
        deadlineNode3.setName("完成规划选址");
//        deadlineNode3.setDescription("负责人");
        deadlineNode3.setTypeName("整改");
        deadlineNode3.setIsEnd("0");
        deadlineNode3.setWarnLevel("0");
        deadlineNode3.setDepartmentName("项目招引单位");
        deadlineNode3.setState("1");
//        deadlineNode3.setDeadlineType("30d");
        deadlineNodes.add(deadlineNode3);
        //完成环评批复
        DeadlineNode deadlineNode4 = new DeadlineNode();
        deadlineNode4.setMainId(rectificationInfo.getId());//mainId
        deadlineNode4.setDeadline(calculateFutureDate(deadline, 60));
        deadlineNode4.setSort(4);
        deadlineNode4.setCreateTime(new Date());
        deadlineNode4.setName("完成环评批复");
        deadlineNode4.setDescription(responsiblePersonsService.locatePerson(deadlineNode4.getName(), rectificationInfo.getLeadUnit()));
        deadlineNode4.setTypeName("整改");
        deadlineNode4.setIsEnd("0");
        deadlineNode4.setRectificationId(rectificationInfo.getId());
        deadlineNode4.setWarnLevel("0");
        deadlineNode4.setDepartmentName("生态环境部门");
        deadlineNode4.setDeadlineType("60d");
        deadlineNode4.setState("1");
        deadlineNodes.add(deadlineNode4);
        //完成安评手续
        DeadlineNode deadlineNode5 = new DeadlineNode();
        deadlineNode5.setMainId(rectificationInfo.getId());//mainId
        deadlineNode5.setDeadline(calculateFutureDate(deadline, 60));
        deadlineNode5.setSort(5);
        deadlineNode5.setCreateTime(new Date());
        deadlineNode5.setName("完成安评手续");
        deadlineNode5.setDescription(responsiblePersonsService.locatePerson(deadlineNode5.getName(), rectificationInfo.getLeadUnit()));
        deadlineNode5.setTypeName("整改");
        deadlineNode5.setIsEnd("0");
        deadlineNode5.setRectificationId(rectificationInfo.getId());
        deadlineNode5.setWarnLevel("0");
        deadlineNode5.setDepartmentName("应急管理部门");
        deadlineNode5.setDeadlineType("60d");
        deadlineNode5.setState("1");
        deadlineNodes.add(deadlineNode5);
        //完成初步设计批复
        DeadlineNode deadlineNode6 = new DeadlineNode();
        deadlineNode6.setMainId(rectificationInfo.getId());//mainId
        deadlineNode6.setDeadline(calculateFutureDate(deadline, 60));
        deadlineNode6.setSort(6);
        deadlineNode6.setCreateTime(new Date());
        deadlineNode6.setName("完成初步设计批复");
        deadlineNode6.setDescription(responsiblePersonsService.locatePerson(deadlineNode6.getName(), rectificationInfo.getLeadUnit()));
        deadlineNode6.setTypeName("整改");
        deadlineNode6.setIsEnd("0");
        deadlineNode6.setWarnLevel("0");
        deadlineNode6.setDepartmentName("发改部门");
        deadlineNode6.setRectificationId(rectificationInfo.getId());
        deadlineNode6.setDeadlineType("60d");
        deadlineNode6.setState("1");
        deadlineNodes.add(deadlineNode6);
        //完成财政预算评审
        DeadlineNode deadlineNode7 = new DeadlineNode();
        deadlineNode7.setMainId(rectificationInfo.getId());//mainId
        deadlineNode7.setDeadline(calculateFutureDate(deadline, 120));
        deadlineNode7.setSort(7);
        deadlineNode7.setCreateTime(new Date());
        deadlineNode7.setName("完成财政预算评审");
        deadlineNode7.setDescription(responsiblePersonsService.locatePerson(deadlineNode7.getName(), rectificationInfo.getLeadUnit()));
        deadlineNode7.setTypeName("整改");
        deadlineNode7.setIsEnd("0");
        deadlineNode7.setWarnLevel("0");
        deadlineNode7.setRectificationId(rectificationInfo.getId());
        deadlineNode7.setDepartmentName("财政部门");
        deadlineNode7.setDeadlineType("120d");
        deadlineNode7.setState("1");
        deadlineNodes.add(deadlineNode7);
        //完成用地手续批复（用地、工程规划许可证）
        DeadlineNode deadlineNode8 = new DeadlineNode();
        deadlineNode8.setMainId(rectificationInfo.getId());//mainId
        deadlineNode8.setDeadline(calculateFutureDate(deadline, 120));
        deadlineNode8.setSort(8);
        deadlineNode8.setCreateTime(new Date());
        deadlineNode8.setName("完成用地手续批复（用地、工程规划许可证）");
        deadlineNode8.setDescription(responsiblePersonsService.locatePerson(deadlineNode8.getName(), rectificationInfo.getLeadUnit()));
        deadlineNode8.setTypeName("整改");
        deadlineNode8.setIsEnd("0");
        deadlineNode8.setRectificationId(rectificationInfo.getId());
        deadlineNode8.setWarnLevel("0");
        deadlineNode8.setDepartmentName("资建部门");
        deadlineNode8.setDeadlineType("120d");
        deadlineNode8.setState("1");
        deadlineNodes.add(deadlineNode8);
        //完成用林手续批复
        DeadlineNode deadlineNode9 = new DeadlineNode();
        deadlineNode9.setMainId(rectificationInfo.getId());//mainId
        deadlineNode9.setDeadline(calculateFutureDate(deadline, 120));
        deadlineNode9.setSort(9);
        deadlineNode9.setCreateTime(new Date());
        deadlineNode9.setName("完成用林手续批复");
        deadlineNode9.setDescription(responsiblePersonsService.locatePerson(deadlineNode9.getName(), rectificationInfo.getLeadUnit()));
        deadlineNode9.setTypeName("整改");
        deadlineNode9.setIsEnd("0");
        deadlineNode9.setRectificationId(rectificationInfo.getId());
        deadlineNode9.setWarnLevel("0");
        deadlineNode9.setDepartmentName("林业部门");
        deadlineNode9.setDeadlineType("120d");
        deadlineNode9.setState("1");
        deadlineNodes.add(deadlineNode9);
        //完成项目招投标（含公示）
        DeadlineNode deadlineNode10 = new DeadlineNode();
        deadlineNode10.setMainId(rectificationInfo.getId());//mainId
        deadlineNode10.setDeadline(calculateFutureDate(deadline, 150));
        deadlineNode10.setSort(10);
        deadlineNode10.setCreateTime(new Date());
        deadlineNode10.setName("完成项目招投标（含公示）");
        deadlineNode10.setDescription(responsiblePersonsService.locatePerson(deadlineNode10.getName(), rectificationInfo.getLeadUnit()));
        deadlineNode10.setTypeName("整改");
        deadlineNode10.setIsEnd("0");
        deadlineNode10.setWarnLevel("0");
        deadlineNode10.setRectificationId(rectificationInfo.getId());
        deadlineNode10.setDepartmentName("项目单位");
        deadlineNode10.setDeadlineType("150d");
        deadlineNode10.setState("1");
        deadlineNodes.add(deadlineNode10);
        //办理施工许可证并开工建设
        DeadlineNode deadlineNode11 = new DeadlineNode();
        deadlineNode11.setMainId(rectificationInfo.getId());//mainId
        deadlineNode11.setDeadline(calculateFutureDate(deadline, 180));
        deadlineNode11.setSort(11);
        deadlineNode11.setCreateTime(new Date());
        deadlineNode11.setName("办理施工许可证并开工建设");
        deadlineNode11.setDescription(responsiblePersonsService.locatePerson(deadlineNode11.getName(), rectificationInfo.getLeadUnit()));
        deadlineNode11.setTypeName("整改");
        deadlineNode11.setIsEnd("0");
        deadlineNode11.setRectificationId(rectificationInfo.getId());
        deadlineNode11.setWarnLevel("0");
        deadlineNode11.setDepartmentName("资建部门");
        deadlineNode11.setDeadlineType("180d");
        deadlineNode11.setState("1");
        deadlineNodes.add(deadlineNode11);
        deadlineNodeMapper.batchInsert(deadlineNodes);
        return "";
    }


    public boolean hasSimilarName(String inputName, List<RectificationInfo> rectificationInfos) {
        if (inputName == null || inputName.isEmpty() || rectificationInfos == null || rectificationInfos.isEmpty()) {
            return false;
        }

        // 提取名称列表
        List<String> names = rectificationInfos.stream()
                .map(RectificationInfo::getTitle) // 假设RectificationInfo有getName()方法
                .filter(Objects::nonNull) // 过滤掉null值
                .collect(Collectors.toList());

        // 遍历所有名称，检查是否有与输入名称相似度超过70%的
        for (String name : names) {
            double similarity = calculateSimilarity(inputName, name);
            if (similarity >= 0.7) {
                return true;
            }
        }

        return false;
    }

    // 计算两个字符串的相似度（使用Levenshtein距离算法）
    private double calculateSimilarity(String s1, String s2) {
        if (s1 == null || s2 == null) {
            return 0;
        }

        if (s1.equals(s2)) {
            return 1.0;
        }

        int maxLength = Math.max(s1.length(), s2.length());
        if (maxLength == 0) {
            return 1.0;
        }

        // 计算Levenshtein距离
        int distance = levenshteinDistance(s1, s2);

        // 计算相似度：1 - (编辑距离 / 最大长度)
        return 1.0 - (double) distance / maxLength;
    }

    // 计算Levenshtein距离（编辑距离）
    private int levenshteinDistance(String s1, String s2) {
        int m = s1.length();
        int n = s2.length();

        int[][] dp = new int[m + 1][n + 1];

        for (int i = 0; i <= m; i++) {
            dp[i][0] = i;
        }

        for (int j = 0; j <= n; j++) {
            dp[0][j] = j;
        }

        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    dp[i][j] = Math.min(Math.min(
                                    dp[i - 1][j] + 1,     // 删除
                                    dp[i][j - 1] + 1),    // 插入
                            dp[i - 1][j - 1] + 1  // 替换
                    );
                }
            }
        }

        return dp[m][n];
    }


    private RectificationV2Vo calculateRate(List<RectificationInfo> rectificationInfos) {
        int completationAndNotOverdue = 0;//正常完成
        int completationAndOverdue = 0;//超时完成
        int notCompletationAndNotOverdue = 0;//正常未完成(=正常推进)
        int notCompletationAndOverdue = 0;//超时未完成(=超期)
        RectificationV2Vo rectificationV2Vo = new RectificationV2Vo();
        int red = 0;
        int green = 0;
        int yellow = 0;
        for (RectificationInfo rectificationInfo : rectificationInfos) {
            if (rectificationInfo.getTitle() == null){
                if (rectificationInfo.getWarnLevel() == null){
                    green++;
                }else if (rectificationInfo.getWarnLevel() == 2){
                    red++;
                }else{
                    yellow++;
                }
                rectificationV2Vo.setRed(red);
                rectificationV2Vo.setGreen(green);
                rectificationV2Vo.setYellow(yellow);
                rectificationV2Vo.setTotal(red + green + yellow);
                continue;
            }
            List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(
                    new LambdaQueryWrapper<DeadlineNode>()
                            .eq(DeadlineNode::getMainId, rectificationInfo.getId())
                            .orderByDesc(DeadlineNode::getId)
            );

            // === 你现有 currentNode 计算（不变）===
            List<DeadlineNode> qualifiedNodes = new ArrayList<>();
            Date today = new Date();
            String currentNode = "";
            // 第一步：筛选符合条件的节点
            for (DeadlineNode d : deadlineNodeList) {
                // 1. 必须有截止日期
                if (d.getDeadline() == null) {
                    continue;
                }
                Date endTime = d.getEndTime();
                boolean endTimeValid = false;
                if (endTime == null) {
                    endTimeValid = true;
                } else {
                    if (endTime.after(today) || endTime.after(d.getDeadline())) {
                        endTimeValid = true;
                    }
                }

                if (endTimeValid) {
                    qualifiedNodes.add(d);
                }
            }

            if (!qualifiedNodes.isEmpty()) {
                // 找到最早的截止日期
                Date earliestDeadline = qualifiedNodes.get(0).getDeadline();
                for (DeadlineNode node : qualifiedNodes) {
                    if (node.getDeadline().before(earliestDeadline)) {
                        earliestDeadline = node.getDeadline();
                    }
                }

                // 拼接所有最早截止日期的节点名称
                StringBuilder sb = new StringBuilder();
                for (DeadlineNode node : qualifiedNodes) {
                    if (node.getDeadline().equals(earliestDeadline)) {
                        sb.append(node.getName()).append("; "); // 用分号分隔多个节点
                    }
                }

                // 移除最后一个分隔符
                if (sb.length() > 0) {
                    currentNode = sb.substring(0, sb.length() - 2);
                }
            }
            if (StrUtil.isEmptyIfStr(currentNode)) {
                for (DeadlineNode d : deadlineNodeList) {
                    if (d.getDeadline() != null) {
                        if (d.getDeadline().after(new Date())) {
                            currentNode = d.getName();
                        }
                    }
                }
            }

            // 第二步：从符合条件的节点中，筛选出最早截止日期的节点
            if (!qualifiedNodes.isEmpty()) {
                // 找到最早的截止日期
                Date earliestDeadline = qualifiedNodes.get(0).getDeadline();
                for (DeadlineNode node : qualifiedNodes) {
                    if (node.getDeadline().before(earliestDeadline)) {
                        earliestDeadline = node.getDeadline();
                    }
                }

                // === 你现有 warnLevel（按“当前环节”）计算（不变）===
                Integer warnLevel = 0;
                for (DeadlineNode node : deadlineNodeList) {
                    boolean isCurrentNode = false;
                    if (currentNode.contains(node.getName())) {
                        isCurrentNode = true;
                        int nodeWarnLevel = Convert.toInt(node.getWarnLevel(), 0);
                        if (nodeWarnLevel == 2) {
                            warnLevel = 2;
                            break;
                        } else if (warnLevel < 1 && nodeWarnLevel == 1) {
                            warnLevel = 1;
                        }
                    }
                    if (isCurrentNode) {
                        int nodeWarnLevel = Convert.toInt(node.getWarnLevel(), 0);
                        if (nodeWarnLevel == 2) {
                            warnLevel = 2;
                            break;
                        } else if (warnLevel < 1 && nodeWarnLevel == 1) {
                            warnLevel = 1;
                        }
                    }
                }
                rectificationInfo.setWarnLevel(warnLevel);
                boolean completation = true;  // 是否全部已完成
                boolean notOverdue = true;  // 是否没有任何超期节点（以 warnLevel=2 作为超期依据）
                if (StrUtil.isEmpty(currentNode)) {
                    completation = true;
                } else {
                    completation = false;
                    if (warnLevel != 2) {
                        notOverdue = true;
                    } else {
                        notOverdue = false;
                    }
                }

                String statusLabel;
                if (completation && notOverdue) {
                    statusLabel = "正常完成";
                } else if (completation) {
                    statusLabel = "超期完成";
                } else if (notOverdue) {
                    statusLabel = "正常推进"; // = 正常未完成
                } else {
                    statusLabel = "超期";     // = 超时未完成
                }
                rectificationInfo.setStatusLabel(statusLabel);

                if (completation && notOverdue) {
                    completationAndNotOverdue++;
                } else if (completation && !notOverdue) {
                    completationAndOverdue++;
                } else if (!completation && notOverdue) {
                    notCompletationAndNotOverdue++;
                } else {
                    notCompletationAndOverdue++;
                }
            }
            if (rectificationInfo.getWarnLevel() != null) {
                if (rectificationInfo.getWarnLevel() == 2) {
                    red++;
                } else if (rectificationInfo.getWarnLevel() == 1) {
                    yellow++;
                } else {
                    green++;
                }
                rectificationV2Vo.setRed(red);
                rectificationV2Vo.setGreen(green);
                rectificationV2Vo.setYellow(yellow);
                rectificationV2Vo.setTotal(red + green + yellow);
            }
        }
        return rectificationV2Vo;
    }

//    private RectificationV2Vo calculateDeadline(List<RectificationInfo> rectificationInfos) {
//        RectificationV2Vo rectificationV2Vo = new RectificationV2Vo();
//        int red = 0;
//        int green = 0;
//        int yellow = 0;
//        List<DeadlineRectificationPeriod> deadlineRectificationPeriods = deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<>());
//
//        for (RectificationInfo rectificationInfo : rectificationInfos) {
////            List<DeadlineNode> deadlineNodeList = deadlineNodeMapper.selectList(
////                    new LambdaQueryWrapper<DeadlineNode>()
////                            .eq(DeadlineNode::getRectificationId, rectificationInfo.getId())
////                            .orderByDesc(DeadlineNode::getId)
////            );
////            deadlineNodeList = filiterNode(rectificationInfo, deadlineNodeList);
////            rectificationInfo = calcWarnLevel(rectificationInfo,deadlineNodeList,deadlineRectificationPeriods);
//
//            // 第一步：筛选符合条件的节点
//            for (DeadlineNode d : deadlineNodeList) {
//                // 1. 必须有截止日期
//                if (d.getDeadline() == null) {
//                    continue;
//                }
//                if (d.getWarnLevel().equals("2")) {
//                    red++;
//                } else if (d.getWarnLevel().equals("1")) {
//                    yellow++;
//                } else {
//                    green++;
//                }
//            }
//            rectificationV2Vo.setRed(red);
//            rectificationV2Vo.setGreen(green);
//            rectificationV2Vo.setYellow(yellow);
//            rectificationV2Vo.setTotal(red + green + yellow);
//        }
//        return rectificationV2Vo;
//    }

    private static final Set<String> COUNTY_LEVEL_DISTRICTS = new HashSet<>(Arrays.asList(
            "黄州区", "团风县", "红安县", "麻城市",
            "罗田县", "英山县", "浠水县", "蕲春县",
            "武穴市", "黄梅县", "黄冈高新区","市临空经济区",
            "龙感湖管理区", "白莲河示范区"
    ));

    private static final Set<String> COUNTY_LEVEL_DISTRICTS2 = new HashSet<>(Arrays.asList(
            "黄州区", "团风县", "红安县", "麻城市",
            "罗田县", "英山县", "浠水县", "蕲春县",
            "武穴市", "黄梅县"
    ));



    /**
     * 查询环节中包含该部门的tree
     * @param currentDepartment
     * @return
     */
    private List<RectificationInfo> fetchAllOnlyNodeRectification(String currentDepartment){
        LambdaQueryWrapper<RectificationInfo> rectificationInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        rectificationInfoLambdaQueryWrapper.ne(RectificationInfo::getState, "已关闭").ne(RectificationInfo::getState, "已销号");
        if(StrUtil.isNotBlank(currentDepartment)){

            // 环节中的部门可能是这种
            // 发改部门 人社部门
            // 但是我们有一个映射关系
            String nodeDepartMent = currentDepartment.replace("市发改委","发改");
            nodeDepartMent = nodeDepartMent.replace("市资建局","资建");
            nodeDepartMent = nodeDepartMent.replace("市林业局","林业");
            nodeDepartMent = nodeDepartMent.replace("市财政局","财政");
            nodeDepartMent = nodeDepartMent.replace("市生态环境局","生态");
            nodeDepartMent = nodeDepartMent.replace("市应急管理局","应急");
            nodeDepartMent = nodeDepartMent.replace("市自然资源规划局","自规部门");
            nodeDepartMent = nodeDepartMent.replace("市住新局","住建");

            // 2. 获取关联ID（所有部门都获取，不再仅市本级）
            List<Integer> relatedRectificationIds = getFilteredTaskIds(nodeDepartMent);
            // 2.3 若关联的mainId不为空，才加OR in条件（避免in()空集合导致SQL错误）
            if (CollectionUtil.isNotEmpty(relatedRectificationIds)) {
                rectificationInfoLambdaQueryWrapper.in(RectificationInfo::getId, relatedRectificationIds);
            }

        }
        rectificationInfoLambdaQueryWrapper.orderByAsc(RectificationInfo::getId);
        List<RectificationInfo> rectificationList = rectificationInfoMapper.selectList(rectificationInfoLambdaQueryWrapper);
        return rectificationList;
    }

    private List<RectificationInfo> exprotList(JSONObject reqJson){
        LambdaQueryWrapper<RectificationInfo> wrapper = new LambdaQueryWrapper<>();
        String state = reqJson.getString("state");
        String projectType = reqJson.getString("projectType");
        String areaName = reqJson.getString("areaName");
        String leadUnit = reqJson.getString("leadUnit");
        String departmentName = reqJson.getString("departmentName");
        String projectChildType = reqJson.getString("projectChildType");
        JSONArray projectChildTypeList = reqJson.getJSONArray("projectChildTypeList");
        String projectChildCategoryType = reqJson.getString("projectChildCategoryType");
        String title = reqJson.getString("title");
        String personLiaible = reqJson.getString("personLiaible");
        String leadLeader = reqJson.getString("leadLeader");
        String currentDepartment = reqJson.getString("currentDepartment");
        String currentNode = reqJson.getString("currentNode");
        String statusType = reqJson.getString("statusType");
        Integer warnLevel = reqJson.getInteger("warnLevel");
        String label = reqJson.getString("label");

        // 1. 获取所有数据
        List<RectificationInfo> rectificationInfoList = fetchAllRectification(currentDepartment);

        // 2. 预先获取所有可能需要的数据
        List<Integer> allIds = rectificationInfoList.stream()
                .map(RectificationInfo::getId)
                .collect(Collectors.toList());

        List<Integer> rectificationIdList =new ArrayList<>();
        //查询满足责任人的全部节点
        if (StringUtils.isNotBlank(personLiaible)) {
            LambdaQueryWrapper<DeadlineNode> lambdaQueryWrapper = new LambdaQueryWrapper();
            lambdaQueryWrapper.like(DeadlineNode::getDescription, personLiaible);
            lambdaQueryWrapper.orderByDesc(DeadlineNode::getSort);
            List<DeadlineNode> deadlineNodes = deadlineNodeMapper.selectList(lambdaQueryWrapper);
            for (int index = 0; index < rectificationInfoList.size(); index++) {
                RectificationInfo rectificationInfo1 = rectificationInfoList.get(index);
                deadlineNodes = filiterNode(rectificationInfo1, deadlineNodes);
            }
            rectificationIdList = deadlineNodes.stream()
                    // 过滤掉null值（避免空指针或后续处理问题）
                    .filter(node -> node.getRectificationId() != null)
                    // 提取rectificationId字段
                    .map(DeadlineNode::getRectificationId)
                    .distinct()
                    // 转换为List
                    .collect(Collectors.toList());
        }

        // 创建final或effectively final的Map变量
        final Map<Integer, List<DeadlineNode>> finalNodeMap;

        if (CollectionUtil.isNotEmpty(allIds)) {
            // 批量查询所有相关的DeadlineNode
            List<DeadlineNode> allNodes = deadlineNodeMapper.selectList(
                    new LambdaQueryWrapper<DeadlineNode>()
                            .in(DeadlineNode::getRectificationId, allIds)
                            .orderByDesc(DeadlineNode::getId)
            );
            finalNodeMap = allNodes.stream()
                    .collect(Collectors.groupingBy(DeadlineNode::getRectificationId));
        } else {
            finalNodeMap = Collections.emptyMap();
        }

        // 获取所有DeadlineRectificationPeriods
        final List<DeadlineRectificationPeriod> finalDeadlinePeriods =
                deadlineRectificationPeriodMapper.selectList(new LambdaQueryWrapper<>());

        // 3. 使用流操作一次性应用所有过滤条件
        Stream<RectificationInfo> stream = rectificationInfoList.stream();

        if (StrUtil.isNotBlank(projectType)) {
            stream = stream.filter(item -> projectType.equals(item.getProjectType()));
        }
        if (StrUtil.isNotBlank(projectChildType)) {
            stream = stream.filter(item ->
                    item.getProjectChildType() != null &&
                            item.getProjectChildType().contains(projectChildType));
        }
        if (StrUtil.isNotBlank(projectChildCategoryType)) {
            stream = stream.filter(item ->
                    item.getProjectChildCategoryType() != null &&
                            item.getProjectChildCategoryType().equals(projectChildCategoryType));
        }
        if (StrUtil.isNotBlank(departmentName)) {
            stream = stream.filter(item ->
                    item.getDepartmentName() != null &&
                            item.getDepartmentName().contains(departmentName));
        }
        if (StrUtil.isNotBlank(title)) {
            stream = stream.filter(item ->
                    item.getTitle() != null &&
                            item.getTitle().equals(title));
        }
        if (StrUtil.isNotBlank(personLiaible)) {
            // 确保rectificationIdList不为null，避免NullPointerException
            List<?> safeIdList = rectificationIdList != null ? rectificationIdList : Collections.emptyList();

            stream = stream.filter(item -> {
                // 避免item为null导致的空指针异常
                if (item == null) {
                    return false;
                }

                // 条件1：personLiaible匹配
                boolean personMatch = item.getPersonLiaible() != null &&
                        item.getPersonLiaible().equals(personLiaible);

                // 条件2：id在列表中
                boolean idMatch = item.getId() != null &&
                        safeIdList.contains(item.getId());

                return personMatch || idMatch;
            });
        }
        if (StrUtil.isNotBlank(leadLeader)) {
            stream = stream.filter(item -> item.getLeadLeader() != null &&
                    item.getLeadLeader().contains(leadLeader));
        }
        if (StrUtil.isNotBlank(label)) {
            stream = stream.filter(item -> item.getLabel() != null &&
                    item.getLabel().contains(label));
        }
        if (StrUtil.isNotBlank(areaName)) {
            final List<String> departments = Arrays.asList(
                    "黄州区", "团风县", "红安县", "麻城市",
                    "罗田县", "英山县", "浠水县", "蕲春县",
                    "武穴市", "黄梅县", "龙感湖管理区", "白莲河示范区",
                    "黄冈高新区","市临空经济区"
            );

            if ("市本级".equals(areaName)) {
                stream = stream.filter(item ->
                        item.getAreaName() != null &&
                                !departments.contains(item.getAreaName()));
            } else {
                stream = stream.filter(item ->
                        item.getAreaName() != null &&
                                item.getAreaName().equals(areaName));
            }
        }
        if (projectChildTypeList != null && projectChildTypeList.size() > 0) {
            final List<String> finalTypeList = projectChildTypeList.toJavaList(String.class);
            stream = stream.filter(item ->
                    item.getProjectChildType() != null &&
                            finalTypeList.contains(item.getProjectChildType()));
        }

        // 应用过滤条件
        rectificationInfoList = stream.collect(Collectors.toList());

        // 4. 并行处理警告级别计算
        if (CollectionUtil.isNotEmpty(rectificationInfoList)) {
            rectificationInfoList = rectificationInfoList.parallelStream()
                    .map(rectificationInfo1 -> {
                        List<DeadlineNode> deadlineNodeList = finalNodeMap.getOrDefault(rectificationInfo1.getId(), new ArrayList<>());
                        deadlineNodeList = filiterNode(rectificationInfo1, deadlineNodeList);
                        RectificationInfo updatedInfo = calcWarnLevel(rectificationInfo1, deadlineNodeList, finalDeadlinePeriods);
                        rectificationInfo1.setWarnLevel(updatedInfo.getWarnLevel());
                        rectificationInfo1.setCurrentNode(updatedInfo.getCurrentNode());
                        return rectificationInfo1;
                    })
                    .collect(Collectors.toList());
        }

        // 5. 应用警告级别和当前节点过滤
        Stream<RectificationInfo> finalStream = rectificationInfoList.stream();

        final Integer finalWarnLevel = warnLevel;
        if (finalWarnLevel != null) {
            finalStream = finalStream.filter(item ->
                    item.getWarnLevel() != null &&
                            item.getWarnLevel().equals(finalWarnLevel));
        }

        final String finalCurrentNode = currentNode;
        if (StrUtil.isNotBlank(finalCurrentNode)) {
            finalStream = finalStream.filter(item ->
                    item.getCurrentNode() != null &&
                            item.getCurrentNode().equals(finalCurrentNode));
        }

        rectificationInfoList = finalStream.collect(Collectors.toList());
        return rectificationInfoList;
    }

    /**
     * 查询环节中包含该部门的tree
     * @param currentDepartment
     * @return
     */
    private List<RectificationInfo> fetchAllNodeRectification(String currentDepartment){
        LambdaQueryWrapper<RectificationInfo> rectificationInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        rectificationInfoLambdaQueryWrapper.ne(RectificationInfo::getState, "已关闭").ne(RectificationInfo::getState, "已销号");
        if(StrUtil.isNotBlank(currentDepartment)){

            // 环节中的部门可能是这种
            // 发改部门 人社部门
            // 但是我们有一个映射关系
            String nodeDepartMent = currentDepartment.replace("市发改委","发改");
            nodeDepartMent = nodeDepartMent.replace("市资建局","资建");
            nodeDepartMent = nodeDepartMent.replace("市林业局","林业");
            nodeDepartMent = nodeDepartMent.replace("市财政局","财政");
            nodeDepartMent = nodeDepartMent.replace("市生态环境局","生态");
            nodeDepartMent = nodeDepartMent.replace("市应急管理局","应急");
            nodeDepartMent = nodeDepartMent.replace("市自然资源规划局","自规部门");
            nodeDepartMent = nodeDepartMent.replace("市住新局","住建");

            // 2. 获取关联ID（所有部门都获取，不再仅市本级）
            List<Integer> relatedRectificationIds = getFilteredTaskIds(nodeDepartMent);

            // 如果是黄州区等.
            if (COUNTY_LEVEL_DISTRICTS.contains(currentDepartment)){
                // 是县市区的
                List<Integer>  departList = getFilteredAreaTaskIds(currentDepartment);
                relatedRectificationIds.addAll(departList);
            }

            rectificationInfoLambdaQueryWrapper.and(wrapper ->
                    wrapper.eq(RectificationInfo::getDepartmentName, currentDepartment)
                            .or()
                            .eq(RectificationInfo::getCreatorUnit, currentDepartment)
                            .or()
                            .eq(RectificationInfo::getLeadUnit, currentDepartment)
            );
            // 2.3 若关联的mainId不为空，才加OR in条件（避免in()空集合导致SQL错误）
            if (CollectionUtil.isNotEmpty(relatedRectificationIds)) {
                rectificationInfoLambdaQueryWrapper.or()
                        .in(RectificationInfo::getId, relatedRectificationIds);
            }

        }
        rectificationInfoLambdaQueryWrapper.orderByAsc(RectificationInfo::getId);
        List<RectificationInfo> rectificationList = rectificationInfoMapper.selectList(rectificationInfoLambdaQueryWrapper);

        List<CompanyData> companyList = companyDataMapper.selectList(new LambdaQueryWrapper<>());
        List<EnterpriseData> enterpriseDataList = enterpriseDataMapper.selectList(new LambdaQueryWrapper<>());
        if(StrUtil.isNotBlank(currentDepartment)){
            // 为了环节的部门功能
//            for (int i = 0; i < rectificationList.size(); i++) {
//                rectificationList.get(i).setDepartmentName(currentDepartment);
//            }
            companyList = companyList.stream().filter(item->item.getDepartmentName().contains(currentDepartment)).collect(Collectors.toList());
            enterpriseDataList = enterpriseDataList.stream().filter(item->item.getDepartmentName().contains(currentDepartment)).collect(Collectors.toList());
        }
        for (int i = 0; i < companyList.size(); i++) {
            RectificationInfo rectificationV2Vo = new RectificationInfo();
            rectificationV2Vo.setProjectType("高质量发展");
            rectificationV2Vo.setProjectChildType("服务企业");
            rectificationV2Vo.setProjectChildCategoryType("重点工业企业包保服务");
            rectificationV2Vo.setTitle(companyList.get(i).getName());
            rectificationV2Vo.setContent(companyList.get(i).getBusiness());
            rectificationV2Vo.setProgressAndProblem(companyList.get(i).getProblem());
            rectificationV2Vo.setDepartmentName(companyList.get(i).getDepartmentName());
            rectificationV2Vo.setCurrentNode("推进中");
            rectificationV2Vo.setState("进行中");
            rectificationV2Vo.setAreaName(companyList.get(i).getDepartmentName());
            rectificationV2Vo.setProgressAndProblem(companyList.get(i).getProblem());
            rectificationV2Vo.setPersonLiaible(companyList.get(i).getPersonResponsible());
            rectificationV2Vo.setIsNotCalc(true);
            if (companyList.get(i).getLampStatus().equals("1")){
                rectificationV2Vo.setWarnLevel(0);
            }else if (companyList.get(i).getLampStatus().equals("2")){
                rectificationV2Vo.setWarnLevel(2);
            }else{
                rectificationV2Vo.setWarnLevel(1);
            }
            rectificationList.add(rectificationV2Vo);
        }
        for (int i = 0; i < enterpriseDataList.size(); i++) {
            RectificationInfo rectificationV2Vo = new RectificationInfo();
            rectificationV2Vo.setProjectType("高质量发展");
            rectificationV2Vo.setProjectChildType("服务企业");
            rectificationV2Vo.setProjectChildCategoryType("服务业市场主体培育");
            rectificationV2Vo.setTitle(enterpriseDataList.get(i).getBusiness());
            rectificationV2Vo.setContent(enterpriseDataList.get(i).getBusiness());
            rectificationV2Vo.setProgressAndProblem(enterpriseDataList.get(i).getProblem());
            rectificationV2Vo.setDepartmentName(enterpriseDataList.get(i).getDepartmentName());
            rectificationV2Vo.setCurrentNode("推进中");
            rectificationV2Vo.setState("进行中");
            rectificationV2Vo.setAreaName(enterpriseDataList.get(i).getDepartmentName());
            rectificationV2Vo.setProgressAndProblem(enterpriseDataList.get(i).getProblem());
            rectificationV2Vo.setPersonLiaible(enterpriseDataList.get(i).getPersonResponsible());
            rectificationV2Vo.setIsNotCalc(true);
            if (enterpriseDataList.get(i).getLampStatus().equals("1")){
                rectificationV2Vo.setWarnLevel(0);
            }else if (enterpriseDataList.get(i).getLampStatus().equals("3")){
                rectificationV2Vo.setWarnLevel(2);
            }else{
                rectificationV2Vo.setWarnLevel(1);
            }
            rectificationList.add(rectificationV2Vo);
        }

        return rectificationList;
    }

    /**
     * 根据部门查询任务ID，同时应用所有过滤条件
     * @param department 部门名称关键词
     * @return 过滤后的任务ID列表
     */
    public List<Integer> getFilteredTaskIds(String department) {
        // 1. 关键：将传入的部门名称转换为简写（适配环节表中可能的简写存储）
        String deptShort = convertToShortDepartment(department);

        // 2. 移除冗余的“市级部门判断”，避免误判（无论层级，只要环节包含部门就应查询）
        // 前置空判断：部门为空时返回空
        if (StrUtil.isBlank(deptShort)) {
            return new ArrayList<>();
        }
        // 直接查询所有环节中包含目标部门的项目ID（rectification_id）
        List<DeadlineNode> allNodes = deadlineNodeMapper.selectList(
                new LambdaQueryWrapper<DeadlineNode>()
                        .like(DeadlineNode::getDepartmentName, deptShort) // 环节部门包含简写
                        .and(wrapper -> wrapper
                                // 条件1：progress 为 null（保留未设置进度的节点）
                                .isNull(DeadlineNode::getProgress)
                                // 条件2：progress 不为 null 且不等于 "/"（排除无效值）
                                .or().ne(DeadlineNode::getProgress, "/")
                        )
                        .select(DeadlineNode::getRectificationId) // 只查必要字段，优化性能
        );

        // 4. 过滤无效进度节点（保留原逻辑，按需调整）
//        List<DeadlineNode> filteredNodes = allNodes.stream()
//                .filter(node -> node.getProgress() == null || !"/".equals(node.getProgress()))
//                .collect(Collectors.toList());
        List<DeadlineNode> filteredNodes = allNodes.stream()
                .filter(node -> {
                    String progress = node.getProgress();
                    // 处理null、空字符串、仅含空格的情况
                    if (progress == null || progress.trim().isEmpty()) {
                        return true; // 保留progress为null或空的节点
                    }
                    // 过滤掉 progress 为 "/" 或 "/ "（带空格）的节点
                    return !"/".equals(progress.trim());
                })
                .collect(Collectors.toList());

        // 5. 提取去重的项目ID
        Set<Integer> rectificationIdSet = filteredNodes.stream()
                .map(DeadlineNode::getRectificationId)
                .filter(Objects::nonNull) // 过滤空ID
                .distinct()
                .collect(Collectors.toSet());

        if (rectificationIdSet.isEmpty()) {
            return new ArrayList<>();
        }

        // 6. 移除“排除县市区项目”的冗余过滤，保留所有符合条件的项目
        // 仅查询项目是否存在（避免重复过滤）
        List<Integer> existingIds = rectificationInfoMapper.selectList(
                        new LambdaQueryWrapper<RectificationInfo>()
                                .in(RectificationInfo::getId, rectificationIdSet)
                                .select(RectificationInfo::getId) // 只查ID，优化性能
                ).stream()
                .map(RectificationInfo::getId)
                .collect(Collectors.toList());

        // 7. 返回最终有效的项目ID
        return new ArrayList<>(existingIds);
    }


    // 新增：部门全称转简写的工具方法（覆盖所有可能的部门）
    private String convertToShortDepartment(String department) {
        if (StrUtil.isBlank(department)) {
            return "";
        }
        return department.replace("市发改委", "发改")
                .replace("市资建局", "资建")
                .replace("市林业局", "林业")
                .replace("市财政局", "财政")
                .replace("市生态环境局", "生态")
                .replace("市应急管理局", "应急")
                .replace("市自然资源规划局", "自规部门")
                .replace("市住新局", "住建")
                .replace("市公安局", "公安") // 补充公安的转换
                .replace("市交通运输局", "交通") // 补充其他部门
                // 继续添加其他部门的转换规则
                ;
    }


    /**
     * 根据县市区查询任务ID，同时应用所有过滤条件
     * @param department 部门名称关键词
     * @return 过滤后的任务ID列表
     */
    public List<Integer> getFilteredAreaTaskIds(String department) {
        // 1. 先查询符合条件的任务ID（来自RectificationInfo的项目类型过滤）
        List<Long> qualifiedTaskIds = getNotQualifiedTaskIdsFromProjectType();
        if (CollectionUtils.isEmpty(qualifiedTaskIds)) {
            return new ArrayList<>();
        }
        // 2. 查询这些任务ID对应的节点，并应用节点级过滤
        List<DeadlineNode> allNodes = deadlineNodeMapper.selectList(
                new LambdaQueryWrapper<DeadlineNode>()
                        .in(DeadlineNode::getRectificationId, qualifiedTaskIds)
                        .like(DeadlineNode::getDepartmentName, department)
        );

        // 3. 过滤节点（排除progress为"/"的节点）
        List<DeadlineNode> filteredNodes = allNodes.stream()
                .filter(node -> node.getProgress() == null || !"/".equals(node.getProgress()))
                .collect(Collectors.toList());
        // 定义需要保留的县市区列表（包含所有目标区域）
        // 4. 按项目类型获取对应的任务ID和允许的节点名称映射
        List<Integer> finalTaskIds = filteredNodes.stream()
                .filter(node -> {
                    // 获取该节点对应的任务信息
                    RectificationInfo rectInfo = rectificationInfoMapper.selectById(node.getRectificationId());
                    if (rectInfo == null) {
                        return false;
                    }
                    return true;
                })
                // 提取任务ID并去重
                .map(DeadlineNode::getRectificationId)
                .distinct()
                .collect(Collectors.toList());

        return finalTaskIds;
    }


    /**
     * 查询高质量发展的项目清单id
     * 查询符合项目类型条件的任务ID
     */
    private List<Long> getQualifiedTaskIdsFromProjectType() {
        LambdaQueryWrapper<RectificationInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(RectificationInfo::getId)
                .and(w -> w
                        // 条件1：主城崛起 + 招商签约未开工重点项目
                        .or(q -> q.eq(RectificationInfo::getProjectType, "高质量发展"))
                );

        return rectificationInfoMapper.selectObjs(queryWrapper).stream()
                .map(obj -> Convert.toLong(obj))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }


    /**
     * 查询不是高质量发展的项目清单id
     * 查询符合项目类型条件的任务ID
     */
    private List<Long> getNotQualifiedTaskIdsFromProjectType() {
        LambdaQueryWrapper<RectificationInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(RectificationInfo::getId)
                .and(w -> w
                        // 条件1：主城崛起 + 招商签约未开工重点项目
                        .or(q -> q.ne(RectificationInfo::getProjectType, "高质量发展"))
                );
        return rectificationInfoMapper.selectObjs(queryWrapper).stream()
                .map(obj -> Convert.toLong(obj))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }


}
